Production Best Practices
This guide covers best practices, performance optimization, and recommended configurations for running Shokupan in production environments.
Configuration
Section titled “Configuration”Basic Production Setup
Section titled “Basic Production Setup”import { Shokupan, Compression, SecurityHeaders, RateLimit } from 'shokupan';
const app = new Shokupan({ port: parseInt(process.env.PORT || '3000'), development: false, enableAsyncLocalStorage: false, // Disable unless needed logger: { verbose: false, info: console.info, debug: () => {}, // Disable debug logs in production warning: console.warn, error: console.error, fatal: console.error }});
// Apply production middlewareapp.use(SecurityHeaders());app.use(RateLimit({ windowMs: 60000, max: 100 }));app.use(Compression({ threshold: 1024 }));
await app.listen();Logging
Section titled “Logging”Structured Logging
Section titled “Structured Logging”Use a structured logging library for better observability:
import { Shokupan } from 'shokupan';import pino from 'pino';
const logger = pino({ level: process.env.LOG_LEVEL || 'info', transport: { target: 'pino-pretty', options: { colorize: false, translateTime: 'SYS:standard', ignore: 'pid,hostname' } }});
const app = new Shokupan({ logger: { verbose: false, info: (msg) => logger.info(msg), debug: (msg) => logger.debug(msg), warning: (msg) => logger.warn(msg), error: (msg) => logger.error(msg), fatal: (msg) => logger.fatal(msg) }});Request Logging Middleware
Section titled “Request Logging Middleware”app.use(async (ctx, next) => { const start = Date.now(); const result = await next(); const duration = Date.now() - start;
logger.info({ method: ctx.request.method, path: ctx.path, status: ctx.response?.status || 200, duration, ip: ctx.request.headers.get('x-forwarded-for') || 'unknown' });
return result;});Performance Optimization
Section titled “Performance Optimization”Enable Compression
Section titled “Enable Compression”[!IMPORTANT] Compression can dramatically improve performance for larger payloads by reducing network I/O overhead.
import { Compression } from 'shokupan';
app.use(Compression({ threshold: 1024 // Only compress responses larger than 1KB}));Why zstd Compression Improves Performance
Section titled “Why zstd Compression Improves Performance”When running on Bun, zstd compression provides significant performance benefits:
Benchmark Results (Shokupan on Bun):
- Without compression: 3,389 req/s
- With zstd compression: 4,120 req/s (~21% improvement)
Compression Ratios:
- Small payloads (1.2 MB): 99.8% reduction (540x compression ratio)
- Large payloads (16 MB): 99.7% reduction (387x compression ratio)
Why it’s faster:
- Reduced network I/O: Transmitting 2-40 KB instead of 1-16 MB
- Less memory operations: Fewer buffer allocations and memory copies through the network stack
- Fast compression: Bun’s native
zstdCompressis extremely efficient - Lower total CPU: The CPU cost of compression is less than the saved I/O processing overhead
[!TIP] For APIs serving JSON responses larger than 10 KB, enable compression for measurable performance gains.
Disable Async Local Storage
Section titled “Disable Async Local Storage”Unless you specifically need AsyncLocalStorage (for request tracing, etc.), disable it:
const app = new Shokupan({ enableAsyncLocalStorage: false // ~5-10% performance improvement});Connection Pooling
Section titled “Connection Pooling”For database connections, use connection pooling:
import { Pool } from 'pg';
const pool = new Pool({ max: 20, idleTimeoutMillis: 30000, connectionTimeoutMillis: 2000,});
// Use the pool in your routesapp.get('/users', async (ctx) => { const { rows } = await pool.query('SELECT * FROM users'); return ctx.json(rows);});Security
Section titled “Security”Security Headers
Section titled “Security Headers”import { SecurityHeaders } from 'shokupan';
app.use(SecurityHeaders({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'"], scriptSrc: ["'self'"], imgSrc: ["'self'", 'data:', 'https:'], } }, strictTransportSecurity: { maxAge: 31536000, includeSubDomains: true, preload: true }}));CORS Configuration
Section titled “CORS Configuration”import { CORS } from 'shokupan';
app.use(CORS({ origin: process.env.ALLOWED_ORIGINS?.split(',') || ['https://yourdomain.com'], credentials: true, maxAge: 86400}));Rate Limiting
Section titled “Rate Limiting”import { RateLimit } from 'shokupan';
// Global rate limitapp.use(RateLimit({ windowMs: 60000, // 1 minute max: 100, // 100 requests per minute standardHeaders: true, legacyHeaders: false}));
// Stricter rate limit for auth endpointsapp.post('/auth/login', RateLimit({ windowMs: 60000, max: 5 }), async (ctx) => { // Login logic });Authentication
Section titled “Authentication”import { Authentication } from 'shokupan';
app.use(Authentication({ secret: process.env.JWT_SECRET!, algorithms: ['HS256'], exclude: ['/health', '/auth/login', '/auth/register']}));Error Handling
Section titled “Error Handling”Global Error Handler
Section titled “Global Error Handler”app.use(async (ctx, next) => { try { return await next(); } catch (error: any) { logger.error({ error: error.message, stack: error.stack, path: ctx.path, method: ctx.request.method });
// Don't expose internal errors in production if (process.env.NODE_ENV === 'production') { return ctx.json({ error: 'Internal Server Error', message: 'An unexpected error occurred' }, 500); }
return ctx.json({ error: error.message, stack: error.stack }, 500); }});Graceful Shutdown
Section titled “Graceful Shutdown”const server = await app.listen(3000);
const shutdown = async () => { logger.info('Shutting down gracefully...');
// Close database connections await pool.end();
// Stop server server.stop();
process.exit(0);};
process.on('SIGTERM', shutdown);process.on('SIGINT', shutdown);Monitoring
Section titled “Monitoring”Health Check Endpoint
Section titled “Health Check Endpoint”app.get('/health', (ctx) => { return ctx.json({ status: 'ok', timestamp: new Date().toISOString(), uptime: process.uptime() });});
app.get('/health/ready', async (ctx) => { try { // Check database connection await pool.query('SELECT 1');
return ctx.json({ status: 'ready' }); } catch (error) { return ctx.json({ status: 'not ready' }, 503); }});Metrics
Section titled “Metrics”let requestCount = 0;let errorCount = 0;
app.use(async (ctx, next) => { requestCount++;
try { return await next(); } catch (error) { errorCount++; throw error; }});
app.get('/metrics', (ctx) => { return ctx.json({ requests: requestCount, errors: errorCount, errorRate: requestCount > 0 ? errorCount / requestCount : 0, uptime: process.uptime(), memory: process.memoryUsage() });});Environment Variables
Section titled “Environment Variables”Required Variables
Section titled “Required Variables”# ServerPORT=3000NODE_ENV=production
# SecurityJWT_SECRET=your-super-secret-jwt-key-change-thisALLOWED_ORIGINS=https://yourdomain.com,https://www.yourdomain.com
# DatabaseDATABASE_URL=postgresql://user:pass@host:5432/dbname
# LoggingLOG_LEVEL=infoLoading Environment Variables
Section titled “Loading Environment Variables”// Bun automatically loads .env files// For Node.js, use dotenv:import 'dotenv/config';
// Validate required variablesconst requiredEnvVars = ['JWT_SECRET', 'DATABASE_URL'];for (const envVar of requiredEnvVars) { if (!process.env[envVar]) { throw new Error(`Missing required environment variable: ${envVar}`); }}Deployment Platforms
Section titled “Deployment Platforms”Docker
Section titled “Docker”See the Deployment Guide for Docker configuration.
Bun with systemd
Section titled “Bun with systemd”Create /etc/systemd/system/myapp.service:
[Unit]Description=My Shokupan ApplicationAfter=network.target
[Service]Type=simpleUser=appuserWorkingDirectory=/opt/myappEnvironment="NODE_ENV=production"EnvironmentFile=/opt/myapp/.envExecStart=/usr/local/bin/bun run src/index.tsRestart=alwaysRestartSec=10
[Install]WantedBy=multi-user.targetEnable and start:
sudo systemctl enable myappsudo systemctl start myappsudo systemctl status myappProduction Checklist
Section titled “Production Checklist”- Logging: Configure structured logging with appropriate log levels
- Compression: Enable compression middleware for responses > 1KB
- Security Headers: Apply security headers plugin
- CORS: Configure CORS with specific allowed origins
- Rate Limiting: Implement rate limiting on all endpoints
- Authentication: Secure private endpoints with JWT or sessions
- Error Handling: Implement global error handler
- Graceful Shutdown: Handle SIGTERM/SIGINT signals
- Health Checks: Add
/healthand/health/readyendpoints - Environment Variables: Use env vars for all secrets and config
- HTTPS: Enable HTTPS (via reverse proxy like nginx/Caddy)
- Database: Use connection pooling
- Monitoring: Set up metrics and alerting
- AsyncLocalStorage: Disable if not needed for performance
- Debug Logs: Disable debug logging in production
Reverse Proxy (nginx)
Section titled “Reverse Proxy (nginx)”Run Shokupan behind nginx for HTTPS and load balancing:
upstream shokupan { server localhost:3000; server localhost:3001; server localhost:3002;}
server { listen 80; server_name yourdomain.com; return 301 https://$server_name$request_uri;}
server { listen 443 ssl http2; server_name yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
location / { proxy_pass http://shokupan; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_cache_bypass $http_upgrade; }}Performance Tips
Section titled “Performance Tips”- Use Bun Runtime: Bun provides the best performance for Shokupan
- Enable zstd Compression: 20%+ performance improvement for JSON APIs
- Minimize Middleware: Only use middleware you actually need
- Connection Pooling: Pool database connections (max 20-50 connections)
- Caching: Use Redis or in-memory caching for frequently accessed data
- Static Assets: Serve static files through CDN or nginx
- Database Indexes: Ensure proper database indexing
- Monitor Memory: Watch for memory leaks with process.memoryUsage()