Getting started with Fetch / Edge Runtimes
UploadThing is compatible with any runtime that follow the WinterCG ↗.
Common setup
Package Setup
Install the package
npm install uploadthing
Add env variables
If you don't already have a uploadthing secret key, sign up ↗ and create one from the dashboard! ↗
UPLOADTHING_TOKEN=... # A token for interacting with the SDK
Set Up A FileRouter
All files uploaded to uploadthing are associated with a FileRoute. The following is a very minimalistic example, with a single FileRoute "imageUploader". Think of a FileRoute similar to an endpoint, it has:
- Permitted types ["image", "video", etc]
- Max file size
- How many files are allowed to be uploaded
- (Optional)
input
validation to validate client-side data sent to the route - (Optional)
middleware
to authenticate and tag requests onUploadComplete
callback for when uploads are completed
To get full insight into what you can do with the FileRoutes, please refer to the File Router API.
import { createUploadthing, type FileRouter } from "uploadthing/server";
const f = createUploadthing();
export const uploadRouter = {
// Define as many FileRoutes as you like, each with a unique routeSlug
imageUploader: f({
image: {
/**
* For full list of options and defaults, see the File Route API reference
* @see https://docs.uploadthing.com/file-routes#route-config
*/
maxFileSize: "4MB",
maxFileCount: 1,
},
}).onUploadComplete((data) => {
console.log("upload completed", data);
}),
} satisfies FileRouter;
export type OurFileRouter = typeof uploadRouter;
Runtimes-specific setup
See configuration options in server API reference
Astro
import { createRouteHandler } from "uploadthing/server";
import { uploadRouter } from "../../server/uploadthing";
const handlers = createRouteHandler({
router: uploadRouter,
config: { ... },
});
export { handlers as GET, handlers as POST };
Read more in our Getting Started with Astro guide.
Elysia
import { Elysia } from "elysia";
import { createRouteHandler } from "uploadthing/server";
import { uploadRouter } from "./uploadthing.ts";
const handlers = createRouteHandler({
router: uploadRouter,
config: { ... },
});
new Elysia()
.get("/api", () => "Hello from Elysia!")
.get("/api/uploadthing", (ev) => handlers(ev.request))
.post("/api/uploadthing", (ev) => handlers(ev.request))
.listen(3000, (server) => {
console.log(`Server listening on port ${server.port}`);
});
Hono
import { Hono } from "hono";
import { createRouteHandler } from "uploadthing/server";
import { uploadRouter } from "./uploadthing.ts";
const handlers = createRouteHandler({
router: uploadRouter,
config: { ... },
});
const app = new Hono();
app.all("/api/uploadthing", (context) => handlers(context.req.raw));
export default app;
Cloudflare Workers
Due to Cloudflare's lack of global environment variables and lack of
init.cache
on fetch
, we need to due some manual setup to make it
compatible.
import { createRouteHandler } from "uploadthing/server";
import { uploadRouter } from "./uploadthing.ts";
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
const handlers = createRouteHandler({
router: uploadRouter,
config: {
/**
* Since workers doesn't have envs on `process`. We need to pass
* secret and isDev flag manually.
*/
token: env.UPLOADTHING_TOKEN,
isDev: env.ENVIRONMENT === "development",
/*
* Cloudflare Workers doesn't support the cache option
* so we need to remove it from the request init.
*/
fetch: (url, init) => {
if (init && "cache" in init) delete init.cache;
return fetch(url, init);
},
/**
* UploadThing dev server leaves some promises hanging around that we
* need to wait for to prevent the worker from exiting prematurely.
*/
handleDaemonPromise: (promise) => ctx.waitUntil(promise),
},
});
// World's simplest router. Handle GET/POST requests to /api/uploadthing
switch (new URL(request.url).pathname) {
case "/api/uploadthing": {
if (request.method !== "POST" && request.method !== "GET") {
return new Response("Method not allowed", { status: 405 });
}
return await handlers[request.method](request);
}
default: {
return new Response("Not found", { status: 404 });
}
}
},
};
Use the FileRouter in your app
Client side usage differs ever so slightly from the fullstack framework setups when using a separate backend server. You'll need to set the URL of your server when you generate the components and helpers.
import { generateUploadButton } from "@uploadthing/react";
export const UploadButton = generateUploadButton({
url: "https://your-server.com/api/uploadthing",
});
// ...
Please note that you might need to setup some CORS rules on your server to allow the client to make requests to the server.
For the remaining usage, please refer to client side examples of the fullstack framework guides:
or check out the full API reference: