Framework Integrations

Bun Runtime

Ultra-fast JavaScript runtime with native Web Standards support - no adapter needed!

Bun Runtime

Bun is an ultra-fast JavaScript runtime with native Web Standards support. Since Bun uses Web Standard Request and Response objects natively, pushduck handlers work directly without any adapters!

Web Standards Native: Bun's Bun.serve() uses Web Standard Request objects directly, making pushduck integration seamless with zero overhead.

Quick Setup

Install dependencies

bun add pushduck
npm install pushduck
yarn add pushduck
pnpm add pushduck

Configure upload router

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!,
  })
  .build();

export const uploadRouter = createS3Router({
  imageUpload: s3.image().max("5MB"),
  documentUpload: s3.file().max("10MB")
});

export type AppUploadRouter = typeof uploadRouter;

Create Bun server with upload routes

server.ts
import { uploadRouter } from './lib/upload';

// Direct usage - no adapter needed!
Bun.serve({
  port: 3000,
  fetch(request) {
    const url = new URL(request.url);
    
    if (url.pathname.startsWith('/api/upload/')) {
      return uploadRouter.handlers(request);
    }
    
    return new Response('Not found', { status: 404 });
  },
});

console.log('🚀 Bun server running on http://localhost:3000');

Basic Integration

Simple Upload Server

server.ts
import { uploadRouter } from './lib/upload';

Bun.serve({
  port: 3000,
  fetch(request) {
    const url = new URL(request.url);
    
    // Method 1: Combined handler (recommended)
    if (url.pathname.startsWith('/api/upload/')) {
      return uploadRouter.handlers(request);
    }
    
    // Health check
    if (url.pathname === '/health') {
      return new Response(JSON.stringify({ status: 'ok' }), {
        headers: { 'Content-Type': 'application/json' }
      });
    }
    
    return new Response('Not found', { status: 404 });
  },
});

console.log('🚀 Bun server running on http://localhost:3000');

With CORS and Routing

server.ts
import { uploadRouter } from './lib/upload';

function handleCORS(request: Request) {
  const origin = request.headers.get('origin');
  const allowedOrigins = ['http://localhost:3000', 'https://your-domain.com'];
  
  const headers = new Headers();
  if (origin && allowedOrigins.includes(origin)) {
    headers.set('Access-Control-Allow-Origin', origin);
  }
  headers.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
  headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  
  return headers;
}

Bun.serve({
  port: 3000,
  fetch(request) {
    const url = new URL(request.url);
    const corsHeaders = handleCORS(request);
    
    // Handle preflight requests
    if (request.method === 'OPTIONS') {
      return new Response(null, { status: 200, headers: corsHeaders });
    }
    
    // Upload routes
    if (url.pathname.startsWith('/api/upload/')) {
      return uploadRouter.handlers(request).then(response => {
        // Add CORS headers to response
        corsHeaders.forEach((value, key) => {
          response.headers.set(key, value);
        });
        return response;
      });
    }
    
    // Health check
    if (url.pathname === '/health') {
      return new Response(JSON.stringify({ 
        status: 'ok', 
        runtime: 'Bun',
        timestamp: new Date().toISOString()
      }), {
        headers: { 
          'Content-Type': 'application/json',
          ...Object.fromEntries(corsHeaders)
        }
      });
    }
    
    return new Response('Not found', { status: 404 });
  },
});

console.log('🚀 Bun server running on http://localhost:3000');

Advanced Configuration

Authentication and Rate Limiting

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({
  // Private uploads with authentication
  privateUpload: s3
    .image()
    .max("5MB")
    .middleware(async ({ req }) => {
      const authHeader = req.headers.get('authorization');
      if (!authHeader?.startsWith('Bearer ')) {
        throw new Error('Authorization required');
      }

      const token = authHeader.substring(7);
      
      try {
        const payload = await verifyJWT(token);
        return {
          userId: payload.sub as string,
          userRole: payload.role as string
        };
      } catch (error) {
        throw new Error('Invalid token');
      }
    }),

  // Public uploads (no auth)
  publicUpload: s3
    .image()
    .max("2MB")
    // No middleware = public access
});

async function verifyJWT(token: string) {
  // Your JWT verification logic here
  // Using Bun's built-in crypto or a JWT library
  return { sub: 'user-123', role: 'user' };
}

export type AppUploadRouter = typeof uploadRouter;

Production Server with Full Features

server.ts
import { uploadRouter } from './lib/upload';

// Simple rate limiting store
const rateLimitStore = new Map<string, { count: number; resetTime: number }>();

function rateLimit(ip: string, maxRequests = 100, windowMs = 15 * 60 * 1000) {
  const now = Date.now();
  const key = ip;
  const record = rateLimitStore.get(key);
  
  if (!record || now > record.resetTime) {
    rateLimitStore.set(key, { count: 1, resetTime: now + windowMs });
    return true;
  }
  
  if (record.count >= maxRequests) {
    return false;
  }
  
  record.count++;
  return true;
}

function getClientIP(request: Request): string {
  // In production, you might get this from headers like X-Forwarded-For
  return request.headers.get('x-forwarded-for') || 
         request.headers.get('x-real-ip') || 
         'unknown';
}

Bun.serve({
  port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
  fetch(request) {
    const url = new URL(request.url);
    const clientIP = getClientIP(request);
    
    // Rate limiting
    if (!rateLimit(clientIP)) {
      return new Response(JSON.stringify({ 
        error: 'Too many requests' 
      }), {
        status: 429,
        headers: { 'Content-Type': 'application/json' }
      });
    }
    
    // CORS
    const corsHeaders = {
      'Access-Control-Allow-Origin': process.env.NODE_ENV === 'production' 
        ? 'https://your-domain.com' 
        : '*',
      'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
      'Access-Control-Allow-Headers': 'Content-Type, Authorization',
    };
    
    // Handle preflight
    if (request.method === 'OPTIONS') {
      return new Response(null, { status: 200, headers: corsHeaders });
    }
    
    // Upload routes
    if (url.pathname.startsWith('/api/upload/')) {
      return uploadRouter.handlers(request).then(response => {
        Object.entries(corsHeaders).forEach(([key, value]) => {
          response.headers.set(key, value);
        });
        return response;
      }).catch(error => {
        console.error('Upload error:', error);
        return new Response(JSON.stringify({ 
          error: 'Upload failed',
          message: process.env.NODE_ENV === 'development' ? error.message : 'Internal server error'
        }), {
          status: 500,
          headers: { 
            'Content-Type': 'application/json',
            ...corsHeaders
          }
        });
      });
    }
    
    // API info
    if (url.pathname === '/api') {
      return new Response(JSON.stringify({
        name: 'Bun Upload API',
        version: '1.0.0',
        runtime: 'Bun',
        endpoints: {
          health: '/health',
          upload: '/api/upload/*'
        }
      }), {
        headers: { 
          'Content-Type': 'application/json',
          ...corsHeaders
        }
      });
    }
    
    // Health check
    if (url.pathname === '/health') {
      return new Response(JSON.stringify({
        status: 'ok',
        runtime: 'Bun',
        version: Bun.version,
        timestamp: new Date().toISOString(),
        uptime: process.uptime()
      }), {
        headers: { 
          'Content-Type': 'application/json',
          ...corsHeaders
        }
      });
    }
    
    return new Response('Not found', { status: 404, headers: corsHeaders });
  },
});

console.log(`🚀 Bun server running on http://localhost:${process.env.PORT || 3000}`);
console.log(`📊 Environment: ${process.env.NODE_ENV || 'development'}`);

File-based Routing

Structured Application

routes/upload.ts
import { uploadRouter } from '../lib/upload';

export function handleUpload(request: Request) {
  return uploadRouter.handlers(request);
}
routes/api.ts
export function handleAPI(request: Request) {
  return new Response(JSON.stringify({
    name: 'Bun Upload API',
    version: '1.0.0',
    runtime: 'Bun'
  }), {
    headers: { 'Content-Type': 'application/json' }
  });
}
server.ts
import { handleUpload } from './routes/upload';
import { handleAPI } from './routes/api';

const routes = {
  '/api/upload': handleUpload,
  '/api': handleAPI,
  '/health': () => new Response(JSON.stringify({ status: 'ok' }), {
    headers: { 'Content-Type': 'application/json' }
  })
};

Bun.serve({
  port: 3000,
  fetch(request) {
    const url = new URL(request.url);
    
    for (const [path, handler] of Object.entries(routes)) {
      if (url.pathname.startsWith(path)) {
        return handler(request);
      }
    }
    
    return new Response('Not found', { status: 404 });
  },
});

Performance Benefits

🚀 Ultra-Fast Runtime

Bun's exceptional performance

Bun is 3x faster than Node.js, providing incredible performance for file upload operations.

⚡ Zero Overhead

Direct Web Standards integration

No adapter layer means zero performance overhead - pushduck handlers run directly in Bun.

📦 Built-in Features

Batteries included

Built-in bundler, test runner, package manager, and more - no extra tooling needed.

🔧 Native TypeScript

First-class TypeScript support

Run TypeScript directly without compilation, perfect for rapid development.

Deployment

Docker Deployment

Dockerfile
FROM oven/bun:1 as base
WORKDIR /usr/src/app

# Install dependencies
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile

# Copy source code
COPY . .

# Expose port
EXPOSE 3000

# Run the app
CMD ["bun", "run", "server.ts"]

Production Scripts

package.json
{
  "name": "bun-upload-server",
  "version": "1.0.0",
  "scripts": {
    "dev": "bun run --watch server.ts",
    "start": "bun run server.ts",
    "build": "bun build server.ts --outdir ./dist --target bun",
    "test": "bun test"
  },
  "dependencies": {
    "pushduck": "latest"
  },
  "devDependencies": {
    "bun-types": "latest"
  }
}

Bun + Pushduck: The perfect combination for ultra-fast file uploads with zero configuration overhead and exceptional developer experience.