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

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 };

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

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",
});
// ...

For the remaining usage, please refer to client side examples of the fullstack framework guides:

or check out the full API reference:

Was this page helpful?