Framework Integrations
Express
Popular Node.js framework integration with pushduck using adapters for req/res API
Express
Express uses the traditional Node.js req
/res
API pattern. Pushduck provides a simple adapter that converts Web Standard handlers to Express middleware format.
Custom Request/Response API: Express uses req
/res
objects instead of Web Standards, so pushduck provides the toExpressHandler
adapter for seamless integration.
Quick Setup
Install dependencies
npm install pushduck
yarn add pushduck
pnpm add pushduck
bun 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 Express server with upload routes
import express from 'express';
import { uploadRouter } from './lib/upload';
import { toExpressHandler } from 'pushduck/adapters/express';
const app = express();
// Convert pushduck handlers to Express middleware
app.all('/api/upload/*', toExpressHandler(uploadRouter.handlers));
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
Basic Integration
Simple Upload Route
import express from 'express';
import cors from 'cors';
import { uploadRouter } from './lib/upload';
import { toExpressHandler } from 'pushduck/adapters/express';
const app = express();
// Middleware
app.use(cors());
app.use(express.json());
// Upload routes using adapter
app.all('/api/upload/*', toExpressHandler(uploadRouter.handlers));
// Health check
app.get('/health', (req, res) => {
res.json({ status: 'healthy', timestamp: new Date().toISOString() });
});
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`🚀 Server running on http://localhost:${port}`);
});
With Authentication Middleware
import express from 'express';
import jwt from 'jsonwebtoken';
import { uploadRouter } from './lib/upload';
import { toExpressHandler } from 'pushduck/adapters/express';
const app = express();
app.use(express.json());
// Authentication middleware
const authenticateToken = (req: express.Request, res: express.Response, next: express.NextFunction) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.sendStatus(401);
}
jwt.verify(token, process.env.JWT_SECRET!, (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
next();
});
};
// Public upload route (no auth)
app.all('/api/upload/public/*', toExpressHandler(uploadRouter.handlers));
// Private upload route (with auth)
app.all('/api/upload/private/*', authenticateToken, toExpressHandler(uploadRouter.handlers));
app.listen(3000);
Advanced Configuration
Upload Configuration with Express Context
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({
// Profile pictures with authentication
profilePicture: s3
.image()
.max("2MB")
.count(1)
.formats(["jpeg", "png", "webp"])
.middleware(async ({ req }) => {
// Extract user from JWT token in Authorization header
const authHeader = req.headers.get('authorization');
if (!authHeader?.startsWith('Bearer ')) {
throw new Error('Authentication required');
}
const token = authHeader.substring(7);
const user = await verifyJWT(token);
return {
userId: user.id,
userRole: user.role,
category: "profile"
};
}),
// Document uploads for authenticated users
documents: s3
.file()
.max("10MB")
.count(5)
.types([
"application/pdf",
"application/msword",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"text/plain"
])
.middleware(async ({ req }) => {
const authHeader = req.headers.get('authorization');
if (!authHeader?.startsWith('Bearer ')) {
throw new Error('Authentication required');
}
const token = authHeader.substring(7);
const user = await verifyJWT(token);
return {
userId: user.id,
category: "documents"
};
}),
// Public uploads (no authentication)
publicImages: s3
.image()
.max("1MB")
.count(1)
.formats(["jpeg", "png"])
// No middleware = public access
});
async function verifyJWT(token: string) {
// Your JWT verification logic
const jwt = await import('jsonwebtoken');
return jwt.verify(token, process.env.JWT_SECRET!) as any;
}
export type AppUploadRouter = typeof uploadRouter;
Complete Express Application
import express from 'express';
import cors from 'cors';
import helmet from 'helmet';
import rateLimit from 'express-rate-limit';
import { uploadRouter } from './lib/upload';
import { toExpressHandler } from 'pushduck/adapters/express';
const app = express();
// Security middleware
app.use(helmet());
app.use(cors({
origin: process.env.NODE_ENV === 'production'
? ['https://your-domain.com']
: ['http://localhost:3000'],
credentials: true
}));
// Rate limiting
const uploadLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
message: 'Too many upload requests from this IP, please try again later.',
standardHeaders: true,
legacyHeaders: false,
});
// Body parsing middleware
app.use(express.json({ limit: '50mb' }));
app.use(express.urlencoded({ extended: true, limit: '50mb' }));
// Logging middleware
app.use((req, res, next) => {
console.log(`${new Date().toISOString()} - ${req.method} ${req.path}`);
next();
});
// Health check endpoint
app.get('/health', (req, res) => {
res.json({
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memory: process.memoryUsage(),
version: process.env.npm_package_version || '1.0.0'
});
});
// API info endpoint
app.get('/api', (req, res) => {
res.json({
name: 'Express Upload API',
version: '1.0.0',
endpoints: {
health: '/health',
upload: '/api/upload/*'
},
uploadTypes: [
'profilePicture - Single profile picture (2MB max)',
'documents - PDF, Word, text files (10MB max, 5 files)',
'publicImages - Public images (1MB max)'
]
});
});
// Upload routes with rate limiting
app.all('/api/upload/*', uploadLimiter, toExpressHandler(uploadRouter.handlers));
// 404 handler
app.use('*', (req, res) => {
res.status(404).json({
error: 'Not Found',
message: `Route ${req.originalUrl} not found`,
timestamp: new Date().toISOString()
});
});
// Error handler
app.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
console.error('Express error:', err);
res.status(500).json({
error: 'Internal Server Error',
message: process.env.NODE_ENV === 'development' ? err.message : 'Something went wrong',
timestamp: new Date().toISOString()
});
});
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`🚀 Express server running on http://localhost:${port}`);
console.log(`📁 Upload endpoint: http://localhost:${port}/api/upload`);
});
Project Structure
server.ts
package.json
.env
Dockerfile
Modular Route Organization
Separate Upload Routes
import { Router } from 'express';
import { uploadRouter } from '../lib/upload';
import { toExpressHandler } from 'pushduck/adapters/express';
import { authenticateToken } from '../middleware/auth';
const router = Router();
// Public uploads
router.all('/public/*', toExpressHandler(uploadRouter.handlers));
// Private uploads (requires authentication)
router.all('/private/*', authenticateToken, toExpressHandler(uploadRouter.handlers));
export default router;
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
export const authenticateToken = (req: Request, res: Response, next: NextFunction) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Access token required' });
}
jwt.verify(token, process.env.JWT_SECRET!, (err, user) => {
if (err) {
return res.status(403).json({ error: 'Invalid or expired token' });
}
req.user = user;
next();
});
};