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
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
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
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
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
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
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
import { uploadRouter } from '../lib/upload';
export function handleUpload(request: Request) {
return uploadRouter.handlers(request);
}
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' }
});
}
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
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
{
"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.