Framework integrations
Mount the route handler on Next.js, Hono, and any Web Request runtime.
createRouteHandler returns a plain
(request: Request) => Promise<Response>. Any framework that gives you a Web
Request and lets you return a Response can mount it as-is. Below are the common
ones.
In every case the handler holds your S3Delivery client (and your secret key),
and the browser uploader points its endpoint at the mounted path (default
/api/s3delivery).
Next.js (App Router)
Export the handler as the POST of a route segment.
// app/api/s3delivery/route.ts
import { S3Delivery, createRouteHandler } from "s3delivery";
export const POST = createRouteHandler(new S3Delivery(), {
constraints: { maxFileSize: 10 * 1024 * 1024, allowedFileTypes: ["image/*"] },
});The browser uploads to /api/s3delivery by default, so no endpoint is needed.
If you mount it elsewhere, set endpoint on the uploader:
useUploadFiles({ endpoint: "/api/uploads" });Next.js (Pages Router)
The handler speaks the Web Request/Response API, but Pages Router API routes
use Node req/res. Prefer an App Router route segment as above. If you must use
the Pages Router, adapt the body yourself with createUpload / complete on the
server client instead of createRouteHandler.
Hono
Pass the raw request through and return the response:
import { Hono } from "hono";
import { S3Delivery, createRouteHandler } from "s3delivery";
const app = new Hono();
const handler = createRouteHandler(new S3Delivery());
app.post("/api/s3delivery", (c) => handler(c.req.raw));Remix
Use the handler in an action.
// app/routes/api.s3delivery.tsx
import type { ActionFunctionArgs } from "@remix-run/node";
import { S3Delivery, createRouteHandler } from "s3delivery";
const handler = createRouteHandler(new S3Delivery());
export const action = ({ request }: ActionFunctionArgs) => handler(request);SvelteKit
// src/routes/api/s3delivery/+server.ts
import { S3Delivery, createRouteHandler } from "s3delivery";
const handler = createRouteHandler(new S3Delivery());
export const POST = ({ request }) => handler(request);Cloudflare Workers / Bun / Deno
These runtimes hand you a Request and want a Response, so the handler is the
whole fetch handler — or one branch of it.
// Cloudflare Workers
import { S3Delivery, createRouteHandler } from "s3delivery";
const handler = createRouteHandler(new S3Delivery());
export default {
async fetch(request: Request): Promise<Response> {
const { pathname } = new URL(request.url);
if (pathname === "/api/s3delivery") return handler(request);
return new Response("Not found", { status: 404 });
},
};// Bun
import { S3Delivery, createRouteHandler } from "s3delivery";
const handler = createRouteHandler(new S3Delivery());
Bun.serve({
fetch(request) {
const { pathname } = new URL(request.url);
if (pathname === "/api/s3delivery") return handler(request);
return new Response("Not found", { status: 404 });
},
});Express (and other Node frameworks)
Express uses Node's req/res, not Web Request/Response. Either upgrade to a
Web-standard adapter, or skip createRouteHandler and broker the handshake with
the server client directly:
import express from "express";
import { S3Delivery } from "s3delivery";
const app = express();
const s3 = new S3Delivery();
app.use(express.json());
app.post("/api/s3delivery", async (req, res) => {
try {
if (req.body.action === "create") {
res.json(await s3.createUpload(req.body));
} else if (req.body.action === "complete") {
res.json(
await s3.complete(req.body.fileId, {
uploadId: req.body.uploadId,
parts: req.body.parts,
}),
);
} else {
res.status(400).json({ error: { code: "BAD_REQUEST", message: "Unknown action" } });
}
} catch (e) {
res.status(500).json({ error: { code: "INTERNAL", message: "Internal error" } });
}
});This matches the body shape the browser uploader sends ({ action, … }), so the
client SDK works against it unchanged. Add your own auth and constraint checks
before createUpload.
Server-only, no browser
If you only ever upload from the server (generated files, agent output, fetched
assets), you do not need a route handler at all — just use
s3.upload(...) directly.