Framework Integrations
Universal file uploads that work with any web framework - from Web Standards to custom request/response APIs
Supported Frameworks
Pushduck provides universal file upload handlers that work with any web framework through a single, consistent API. Write your upload logic once and deploy it anywhere!
Universal Design: Pushduck uses Web Standards (Request/Response) at its core, making it compatible with both Web Standards frameworks and those with custom request/response APIs without framework-specific code.
Universal API
All frameworks use the same core API:
import { createS3Router, s3 } from 'pushduck/server';
const uploadRouter = createS3Router({
imageUpload: s3.image().maxFileSize("5MB"),
documentUpload: s3.file().maxFileSize("10MB"),
videoUpload: s3.file().maxFileSize("100MB").types(["video/*"])
});
// Universal handlers - work with ANY framework
export const { GET, POST } = uploadRouter.handlers;Framework Categories
Pushduck supports frameworks in two categories:
Web Standards Native
Modern frameworks using Web Request/Response
No adapter needed! Use uploadRouter.handlers directly.
- Hono
- Elysia
- Bun Runtime
- TanStack Start
- SolidJS Start
Custom Request/Response APIs
Frameworks with their own request/response patterns
Simple adapters provided for straightforward integration.
- Next.js (App & Pages Router)
- Express
- Fastify
Quick Start by Framework
// Works with: Hono, Elysia, Bun, TanStack Start, SolidJS Start
import { uploadRouter } from '@/lib/upload';
// Direct usage - no adapter needed!
app.all('/api/upload/*', (ctx) => {
return uploadRouter.handlers(ctx.request); // or c.req.raw
});// app/api/upload/route.ts
import { uploadRouter } from '@/lib/upload';
// Direct usage (recommended)
export const { GET, POST } = uploadRouter.handlers;
// Or with explicit adapter for extra type safety
import { toNextJsHandler } from 'pushduck/adapters';
export const { GET, POST } = toNextJsHandler(uploadRouter.handlers);import express from 'express';
import { uploadRouter } from '@/lib/upload';
import { toExpressHandler } from 'pushduck/adapters';
const app = express();
app.all("/api/upload/*", toExpressHandler(uploadRouter.handlers));import Fastify from 'fastify';
import { uploadRouter } from '@/lib/upload';
import { toFastifyHandler } from 'pushduck/adapters';
const fastify = Fastify();
fastify.all('/api/upload/*', toFastifyHandler(uploadRouter.handlers));Why Universal Handlers Work
Web Standards Foundation
Pushduck is built on Web Standards (Request and Response objects) that are supported by all modern JavaScript runtimes.
// Core handler signature
type Handler = (request: Request) => Promise<Response>Framework Compatibility
Modern frameworks expose Web Standard objects directly:
- Hono:
c.req.rawis a WebRequest - Elysia:
context.requestis a WebRequest - Bun: Native Web
Requestsupport - TanStack Start:
{ request }is a WebRequest - SolidJS Start:
event.requestis a WebRequest
Framework Adapters
For frameworks with custom request/response APIs, simple adapters convert between formats:
// Express adapter example
export function toExpressHandler(handlers: UniversalHandlers) {
return async (req: Request, res: Response, next: NextFunction) => {
const webRequest = convertExpressToWebRequest(req);
const webResponse = await handlers[req.method](webRequest);
convertWebResponseToExpress(webResponse, res);
};
}Configuration (Same for All Frameworks)
Your upload configuration is identical across all frameworks:
import { createUploadConfig } from 'pushduck/server';
const { s3, createS3Router } = createUploadConfig()
.provider("cloudflareR2",{
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
region: 'auto',
endpoint: process.env.AWS_ENDPOINT_URL!,
bucket: process.env.S3_BUCKET_NAME!,
accountId: process.env.R2_ACCOUNT_ID!,
})
.paths({
prefix: 'uploads',
generateKey: (file, metadata) => {
return `${metadata.userId}/${Date.now()}/${file.name}`;
}
})
.build();
export const uploadRouter = createS3Router({
// Image uploads with validation
imageUpload: s3
.image()
.maxFileSize("5MB")
.formats(["jpeg", "png", "webp"])
.middleware(async ({ req }) => {
const userId = await getUserId(req);
return { userId, category: "images" };
}),
// Document uploads
documentUpload: s3
.file()
.maxFileSize("10MB")
.types(["application/pdf", "text/plain"])
.middleware(async ({ req }) => {
const userId = await getUserId(req);
return { userId, category: "documents" };
}),
// Video uploads
videoUpload: s3
.file()
.maxFileSize("100MB")
.types(["video/mp4", "video/quicktime"])
.middleware(async ({ req }) => {
const userId = await getUserId(req);
return { userId, category: "videos" };
})
});
export type AppUploadRouter = typeof uploadRouter;Client Usage (Framework Independent)
The client-side code is identical regardless of your backend framework:
import { createUploadClient } from 'pushduck/client';
import type { AppUploadRouter } from './upload';
export const upload = createUploadClient<AppUploadRouter>({
endpoint: '/api/upload'
});import { upload } from '@/lib/upload-client';
export function UploadForm() {
// Property-based access with full type safety
const { uploadFiles, files, isUploading } = upload.imageUpload();
const handleUpload = async (selectedFiles: File[]) => {
await uploadFiles(selectedFiles);
};
return (
<div>
<input
type="file"
multiple
onChange={(e) => handleUpload(Array.from(e.target.files || []))}
/>
{files.map(file => (
<div key={file.id}>
<span>{file.name}</span>
<progress value={file.progress} max={100} />
{file.url && <a href={file.url}>View</a>}
</div>
))}
</div>
);
}Benefits of Universal Design
Framework Flexibility
Switch frameworks without rewriting upload logic
Migrate from Express to Hono or Next.js to Bun without changing your upload implementation.
Performance
Zero overhead for modern frameworks
Web Standards native frameworks get direct handler access with no adapter overhead.
Developer Experience
One API to learn, works everywhere
Master pushduck once and use it with any framework in your toolkit.
Future Proof
Built on Web Standards
As more frameworks adopt Web Standards, they automatically work with pushduck.
Next Steps
Choose your framework integration guide:
Next.js
Complete guide for Next.js App Router and Pages Router
Hono
Fast, lightweight, built on Web Standards
Elysia
TypeScript-first framework with Bun
Express
Classic Node.js framework integration
Universal by Design: Write once, run anywhere. Pushduck's universal handlers make file uploads work across the entire JavaScript ecosystem.