Framework Integrations

Overview

Universal file uploads that work with any web framework - from Web Standards to custom request/response APIs

Framework Integrations Overview

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().max("5MB"),
  documentUpload: s3.file().max("10MB"),
  videoUpload: s3.file().max("100MB").types(["video/*"])
});

// Universal handlers - work with ANY framework
export const { GET, POST } = uploadRouter.handlers;

Framework Categories

Pushduck supports frameworks in two categories:

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/nextjs';
export const { GET, POST } = toNextJsHandler(uploadRouter.handlers);
import express from 'express';
import { uploadRouter } from '@/lib/upload';
import { toExpressHandler } from 'pushduck/adapters/express';

const app = express();
app.all("/api/upload/*", toExpressHandler(uploadRouter.handlers));
import Fastify from 'fastify';
import { uploadRouter } from '@/lib/upload';
import { toFastifyHandler } from 'pushduck/adapters/fastify';

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.raw is a Web Request
  • Elysia: context.request is a Web Request
  • Bun: Native Web Request support
  • TanStack Start: { request } is a Web Request
  • SolidJS Start: event.request is a Web Request

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:

lib/upload.ts
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()
    .max("5MB")
    .formats(["jpeg", "png", "webp"])
    .middleware(async ({ req }) => {
      const userId = await getUserId(req);
      return { userId, category: "images" };
    }),

  // Document uploads
  documentUpload: s3
    .file()
    .max("10MB")
    .types(["application/pdf", "text/plain"])
    .middleware(async ({ req }) => {
      const userId = await getUserId(req);
      return { userId, category: "documents" };
    }),

  // Video uploads
  videoUpload: s3
    .file()
    .max("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:

lib/upload-client.ts
import { createUploadClient } from 'pushduck/client';
import type { AppUploadRouter } from './upload';

export const upload = createUploadClient<AppUploadRouter>({
  endpoint: '/api/upload'
});
components/upload-form.tsx
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:


Universal by Design: Write once, run anywhere. Pushduck's universal handlers make file uploads work seamlessly across the entire JavaScript ecosystem.