Handlers
Mount upload-stuff in Next.js, NestJS, Express, or any fetch runtime.
upload-stuff's HTTP layer is a single fetch handler: (request: Request) => Promise<Response>. It serves three JSON actions under basePath (default /api/upload-stuff): GET /:endpoint/route-config, POST /:endpoint/init-upload, and POST /:endpoint/complete-upload. Every framework integration is a wrapper around that fetch handler. File bytes never pass through your server; they go straight from the browser to storage through presigned URLs.
Next.js (App Router)
import { toNextJsHandler } from "@upload-stuff/server/next";
import { uploadStuff } from "@/lib/upload-stuff";
import { fileRouter } from "@/lib/file-router";
export const { GET, POST } = toNextJsHandler({
uploadStuff,
fileRouter,
createContext: async ({ headers }) => ({ userId: await getUserIdOrNull(headers) }),
});Any fetch runtime
import { toFetchHandler } from "@upload-stuff/server";
const handler = toFetchHandler({ uploadStuff, fileRouter, createContext });
// Bun
Bun.serve({ fetch: handler });
// Cloudflare Workers
export default { fetch: handler };Express
import express from "express";
import { toNodeHandler } from "@upload-stuff/server/node";
const app = express();
app.use("/api/upload-stuff", toNodeHandler({ uploadStuff, fileRouter, createContext }));Mount the middleware at the same path as basePath (default /api/upload-stuff). If express.json() already consumed the request stream, the Node wrapper reuses req.body; if the framework exposes req.originalUrl, the wrapper keeps the original mounted path for routing.
NestJS (Express platform)
Register the handler as middleware in your bootstrap:
import { NestFactory } from "@nestjs/core";
import { toNodeHandler } from "@upload-stuff/server/node";
import { AppModule } from "./app.module";
const app = await NestFactory.create(AppModule);
app.use("/api/upload-stuff", toNodeHandler({ uploadStuff, fileRouter, createContext }));
await app.listen(3000);createContext receives the request headers on every base-path request. Resolve identity there, then enforce authorization in route middleware.
Error contract
| Condition | Response |
|---|---|
Handled UploadStuffError | Its mapped status (400/401/403) with { "error": string } |
| Unknown endpoint on a known action | 400 |
| Bad path or wrong method | 404 (HEAD is served through the GET path with an empty body) |
| Unexpected error | Propagates to the host runtime; toNodeHandler forwards to next when present, else 500 |
Envelope and codes: Errors.