Skip to content

Bun-based HTTP framework inspired by 0http

License

Notifications You must be signed in to change notification settings

BackendStack21/0http-bun

Repository files navigation

0http-bun

npm version license

A high-performance, minimalist HTTP framework for Bun, inspired by 0http. Built specifically to leverage Bun's native performance capabilities with a developer-friendly API.

Landing page: 0http-bun.21no.de

v1.3.0 includes a comprehensive security hardening release. See the Changelog and Migration Guide sections below.

✨ Why Choose 0http-bun?

0http-bun combines the simplicity of Express with the raw performance of Bun's runtime, delivering a framework that's both blazingly fast and secure by design. Perfect for everything from quick prototypes to production-grade APIs.

🚀 Unmatched Performance

  • Bun-Native Optimization: Built specifically for Bun's runtime with zero overhead
  • Lightning-Fast Routing: Based on the proven trouter library with intelligent caching
  • Memory Efficient: Smart object reuse and minimal allocations
  • Optimized Parsing: Uses fast-querystring for lightning-quick query string handling

🎯 Developer Experience

  • TypeScript First: Full type safety with comprehensive definitions
  • Intuitive API: Clean, expressive syntax that's easy to learn
  • Flexible Middleware: Powerful async/await middleware system
  • Web Standards: Built on standard Request/Response APIs

🛡️ Security by Default

  • Production-Ready Security: Built-in protection against common vulnerabilities
  • Input Validation: Comprehensive sanitization and size limits
  • Attack Prevention: Prototype pollution, ReDoS, and DoS protection
  • Secure Defaults: Safe error handling and CORS configuration

Key Benefits

  • 🚀 Bun-Native Performance: Optimized for Bun's runtime with minimal overhead
  • 🔧 TypeScript First: Full TypeScript support with comprehensive type definitions
  • 🎯 Minimalist API: Clean, intuitive API that's easy to learn and use
  • 🔄 Middleware Support: Flexible middleware system with async/await support
  • 📦 Tiny Footprint: Lightweight framework focused on performance
  • 🛡️ Web Standards: Built on standard Web APIs (Request/Response)

Installation

bun add 0http-bun

Quick Start

Basic Server

import http from '0http-bun'

const {router} = http()

router.get('/', () => {
  return new Response('Hello World!')
})

router.get('/:id', (req) => {
  return Response.json({id: req.params.id})
})

// Start the server
Bun.serve({
  port: 3000,
  fetch: router.fetch,
})

Enabling Client IP for Rate Limiting

Bun's standard Request object does not expose the client IP address. To enable the default rate limiter key generator (and any middleware that reads req.ip), use server.requestIP() in the Bun.serve fetch handler:

import http from '0http-bun'
import {createRateLimit} from '0http-bun/lib/middleware'

const {router} = http()

router.use(createRateLimit({windowMs: 15 * 60 * 1000, max: 100}))

router.get('/', () => new Response('Hello World!'))

// Populate req.ip from Bun's server.requestIP before passing to the router
Bun.serve({
  port: 3000,
  fetch(req, server) {
    req.ip = server.requestIP(req)?.address
    return router.fetch(req)
  },
})

Why is this needed? The Fetch API Request type does not include connection-level properties like ip. Bun provides client IP via server.requestIP(req) in the fetch handler's second argument. Setting req.ip before calling router.fetch ensures the rate limiter (and other middleware) can identify clients correctly. Without this, the default key generator falls back to unique per-request keys, which effectively disables rate limiting.

With TypeScript Types

import http, {ZeroRequest, StepFunction} from '0http-bun'

const {router} = http({
  port: 3000,
  errorHandler: (err: Error) => {
    console.error('Server error:', err)
    return new Response('Internal Server Error', {status: 500})
  },
})

// Typed middleware
router.use((req: ZeroRequest, next: StepFunction) => {
  req.ctx = {
    startTime: Date.now(),
    engine: 'bun',
  }
  return next()
})

// Typed route handlers
router.get('/:id', async (req: ZeroRequest) => {
  return Response.json({
    id: req.params.id,
    context: req.ctx,
  })
})

router.post('/users', async (req: ZeroRequest) => {
  const body = await req.json()
  return Response.json({created: true, data: body}, {status: 201})
})

API Reference

Router Configuration

interface IRouterConfig {
  defaultRoute?: RequestHandler // Custom 404 handler
  errorHandler?: (err: Error) => Response | Promise<Response> // Error handler
  port?: number // Port number (for reference)
  cacheSize?: number // Max entries per HTTP method in the route cache (default: 1000)
}

Note on cacheSize: The router uses an LRU-style cache for resolved routes. When the cache exceeds cacheSize entries for a given HTTP method, the oldest entry is evicted. The default of 1000 is suitable for most applications. Increase it for APIs with many dynamic routes; decrease it to save memory in constrained environments.

Request Object

The ZeroRequest extends the standard Request with additional properties:

type ZeroRequest = Request & {
  params: Record<string, string> // URL parameters
  query: Record<string, string> // Query string parameters
  ctx?: Record<string, any> // Custom context (set by middleware)
}

Route Methods

// HTTP Methods
router.get(pattern, ...handlers)
router.post(pattern, ...handlers)
router.put(pattern, ...handlers)
router.patch(pattern, ...handlers)
router.delete(pattern, ...handlers)
router.head(pattern, ...handlers)
router.options(pattern, ...handlers)
router.connect(pattern, ...handlers)
router.trace(pattern, ...handlers)

// Generic method
router.on(method, pattern, ...handlers)

// All methods
router.all(pattern, ...handlers)

Middleware

// Global middleware
router.use((req, next) => {
  // Middleware logic
  return next()
})

// Path-specific middleware
router.use('/api/*', (req, next) => {
  // API-specific middleware
  return next()
})

// Multiple middlewares
router.use(authMiddleware, loggingMiddleware, (req, next) => next())

Examples

Complete REST API

import http, {ZeroRequest, StepFunction} from '0http-bun'

const {router} = http({
  errorHandler: (err: Error) => {
    return Response.json({error: err.message}, {status: 500})
  },
})

// Logging middleware
router.use((req: ZeroRequest, next: StepFunction) => {
  console.log(`${req.method} ${req.url}`)
  return next()
})

// JSON body parser middleware for POST/PUT
router.use('/api/*', async (req: ZeroRequest, next: StepFunction) => {
  if (req.method === 'POST' || req.method === 'PUT') {
    try {
      req.ctx = {...req.ctx, body: await req.json()}
    } catch (err) {
      return Response.json({error: 'Invalid JSON'}, {status: 400})
    }
  }
  return next()
})

// Routes
router.get('/api/users', () => {
  return Response.json([
    {id: 1, name: 'John'},
    {id: 2, name: 'Jane'},
  ])
})

router.get('/api/users/:id', (req: ZeroRequest) => {
  const {id} = req.params
  return Response.json({id: Number(id), name: 'User'})
})

router.post('/api/users', (req: ZeroRequest) => {
  const userData = req.ctx?.body
  return Response.json({id: Date.now(), ...userData}, {status: 201})
})

router.delete('/api/users/:id', (req: ZeroRequest) => {
  const {id} = req.params
  return Response.json({deleted: id})
})

// Start server
Bun.serve({
  port: 3000,
  fetch: router.fetch,
})

Middleware Support

0http-bun includes a comprehensive middleware system with built-in middlewares for common use cases:

📦 Note: Starting with v1.2.2, some middleware dependencies are optional. Install only what you need: jose (JWT), pino (Logger), prom-client (Prometheus).

  • Body Parser - Automatic request body parsing (JSON, form data, text)
  • CORS - Cross-Origin Resource Sharing with flexible configuration
  • JWT Authentication - JSON Web Token authentication and authorization
  • Logger - Request logging with multiple output formats
  • Rate Limiting - Flexible rate limiting with sliding window support
  • Prometheus Metrics - Export metrics for monitoring and alerting

Quick Example

// Import middleware functions from the middleware module
const {
  createCORS,
  createLogger,
  createBodyParser,
  createJWTAuth,
  createRateLimit,
} = require('0http-bun/lib/middleware')

const {router} = http()

// Apply middleware stack
router.use(createCORS()) // Enable CORS
router.use(createLogger()) // Request logging
router.use(createBodyParser()) // Parse request bodies
router.use(createRateLimit({max: 100})) // Rate limiting

// Protected routes
router.use('/api/*', createJWTAuth({secret: process.env.JWT_SECRET}))

📖 Complete Middleware Documentation

Error Handling

The default error handler returns a generic "Internal Server Error" response (HTTP 500) and logs the full error to console.error. This prevents leaking stack traces or internal details to clients.

You can provide a custom errorHandler for more control:

import http, {ZeroRequest} from '0http-bun'

const {router} = http({
  errorHandler: (err: Error) => {
    console.error('Application error:', err)

    // Custom error responses based on error type
    if (err.name === 'ValidationError') {
      return Response.json(
        {error: 'Validation failed', details: err.message},
        {status: 400},
      )
    }

    return Response.json({error: 'Internal server error'}, {status: 500})
  },
  defaultRoute: () => {
    return Response.json({error: 'Route not found'}, {status: 404})
  },
})

// Errors thrown in both sync and async handlers are caught automatically
router.get('/api/risky', async (req: ZeroRequest) => {
  const data = await fetchExternalData() // async errors are caught too
  if (!data) {
    throw new Error('Data not found')
  }
  return Response.json(data)
})

Async error handling: Errors thrown or rejected in async middleware/handlers are automatically caught and forwarded to the errorHandler. You do not need to wrap every handler in try/catch.

Security

0http-bun is designed with security-first principles and includes comprehensive protection against common web vulnerabilities. The core framework and middleware have been thoroughly penetration-tested and hardened.

Built-in Security Features

Input Validation & Sanitization

  • Size Limits: Configurable limits prevent memory exhaustion attacks
  • ReDoS Protection: Restrictive regex patterns prevent Regular Expression DoS
  • JSON Security: String-aware nesting depth validation and safe parsing
  • Parameter Validation: Maximum parameter counts and length restrictions
  • Filename Sanitization: Multipart file uploads are sanitized to prevent path traversal (directory separators, .., null bytes are stripped; originalName preserved for reference)

Attack Prevention

  • Prototype Pollution Protection: Filters dangerous keys (__proto__, constructor, prototype) in body parser, multipart parser, and URL-encoded parser using Object.create(null) for safe objects
  • Timing Attack Prevention: API key comparisons use crypto.timingSafeEqual()
  • Algorithm Confusion Prevention: JWT middleware rejects mixed symmetric/asymmetric algorithm configurations
  • Cache Exhaustion Prevention: LRU-style route cache with configurable cacheSize limit (default: 1000)
  • Memory Exhaustion Prevention: Strict size limits, sliding window rate limiter with maxKeys eviction, and automatic cleanup intervals
  • Route Filter Bypass Prevention: URL path normalization (double-slash collapse, URI decoding, %2F preservation)
  • Frozen Route Params: Parameterless routes receive an immutable Object.freeze({}) to prevent cross-request data leakage

Error Handling

  • Generic Error Responses: Default error handler returns "Internal Server Error" without exposing stack traces or err.message
  • Server-Side Logging: Full error details logged via console.error for debugging
  • Async Error Catching: Rejected promises from async middleware are automatically caught and forwarded to the error handler
  • Unified JWT Errors: JWT signature/expiry/claim validation failures return "Invalid or expired token" regardless of the specific failure reason

Authentication & Authorization

  • JWT Security: Default algorithms restricted to ['HS256']; algorithm confusion prevented
  • Timing-Safe API Keys: All API key comparisons use constant-time comparison
  • Path Exclusion Security: Uses exact match or path boundary checking (path + '/') instead of prefix matching
  • Optional Mode Visibility: When optional: true, invalid tokens set req.ctx.authError and req.ctx.authAttempted for downstream inspection
  • Minimal Token Exposure: Raw JWT token is no longer stored on the request context

Rate Limiting

  • Secure Key Generation: Default key generator uses req.ip || req.remoteAddress || req.socket?.remoteAddress || 'unknown' — proxy headers are not trusted by default
  • No Store Injection: Rate limit store is always the constructor-configured instance (no req.rateLimitStore override)
  • Bounded Memory: Sliding window rate limiter enforces maxKeys (default: 10,000) with periodic cleanup
  • Synchronous Increment: MemoryStore.increment is synchronous to eliminate TOCTOU race conditions
  • Configurable Limits: Flexible rate limiting with skip functions and path exclusion

CORS Security

  • Null Origin Rejection: null and missing origins are rejected before calling validator functions or checking arrays, preventing sandboxed iframe bypass
  • Conditional Headers: CORS headers (methods, allowed headers, credentials, exposed headers) are only set when the origin is actually allowed
  • Vary Header: Vary: Origin is set for all non-wildcard origins to prevent CDN cache poisoning
  • Credential Safety: Prevents wildcard origins with credentials

Security Best Practices

// Secure server configuration
const {router} = http({
  cacheSize: 500, // Limit route cache for constrained environments
  errorHandler: (err: Error) => {
    // Never expose stack traces in production
    return Response.json({error: 'Internal server error'}, {status: 500})
  },
  defaultRoute: () => {
    return Response.json({error: 'Not found'}, {status: 404})
  },
})

// Apply security middleware stack
router.use(
  createCORS({
    origin: ['https://yourdomain.com'], // Restrict origins — null origins are rejected
    credentials: true,
  }),
)

router.use(
  createRateLimit({
    windowMs: 15 * 60 * 1000, // 15 minutes
    max: 100,
    // Behind a reverse proxy? Provide a custom keyGenerator:
    // keyGenerator: (req) => req.headers.get('x-forwarded-for') || req.ip || 'unknown',
  }),
)

router.use(
  '/api/*',
  createJWTAuth({
    secret: process.env.JWT_SECRET,
    algorithms: ['HS256'], // Default — explicit for clarity
    // For RS256, use jwksUri instead of secret:
    // jwksUri: 'https://your-idp.com/.well-known/jwks.json',
    // algorithms: ['RS256'],
  }),
)

// Secure body parsing
router.use(
  createBodyParser({
    json: {limit: '10mb'},
    urlencoded: {limit: '10mb'},
    multipart: {limit: '50mb'},
  }),
)

Security Monitoring

0http-bun provides built-in security monitoring capabilities:

// Security logging with request context
router.use(
  createLogger({
    serializers: {
      req: (req) => ({
        method: req.method,
        url: req.url,
        ip:
          req.ip || req.remoteAddress || req.socket?.remoteAddress || 'unknown',
        userAgent: req.headers.get('user-agent'),
      }),
    },
  }),
)

// Prometheus metrics for security monitoring
router.use(
  createPrometheusMetrics({
    prefix: 'http_',
    labels: ['method', 'route', 'status_code'],
  }),
)

Security Recommendations

  1. Environment Variables: Store secrets in environment variables, never in code
  2. HTTPS Only: Always use HTTPS in production with proper TLS configuration
  3. Reverse Proxy Configuration: If behind a reverse proxy, always provide a custom keyGenerator for rate limiting that reads the appropriate header (e.g., X-Forwarded-For)
  4. Algorithm Explicitness: Always explicitly set JWT algorithms — do not rely on defaults
  5. Input Validation: Validate and sanitize all user inputs
  6. Regular Updates: Keep dependencies updated and run security audits
  7. Monitoring: Implement logging and monitoring for security events

Security is a continuous process. While 0http-bun provides strong security foundations, always follow security best practices and conduct regular security assessments for your applications.

Performance

0http-bun is designed for high performance with Bun's native capabilities:

  • Minimal overhead: Direct use of Web APIs
  • Efficient routing: Based on the proven trouter library
  • Fast parameter parsing: Optimized URL parameter extraction with caching
  • Query string parsing: Uses fast-querystring for optimal performance
  • Memory efficient: LRU-style route caching with configurable cacheSize limit, immutable shared objects, and minimal allocations
  • URL normalization: Single-pass URL parsing with path normalization (double-slash collapse, URI decoding)

Benchmark Results

Run benchmarks with:

bun run bench

Performance characteristics will vary based on your specific use case and middleware stack.

TypeScript Support

Full TypeScript support is included with comprehensive type definitions:

// Main framework types
import {
  ZeroRequest,
  StepFunction,
  RequestHandler,
  IRouter,
  IRouterConfig,
} from '0http-bun'

// Middleware-specific types
import {
  LoggerOptions,
  JWTAuthOptions,
  APIKeyAuthOptions,
  RateLimitOptions,
  CORSOptions,
  BodyParserOptions,
  MemoryStore,
} from '0http-bun/lib/middleware'

// Example typed middleware
const customMiddleware: RequestHandler = (
  req: ZeroRequest,
  next: StepFunction,
) => {
  req.ctx = req.ctx || {}
  req.ctx.timestamp = Date.now()
  return next()
}

// Example typed route handler
const typedHandler = (req: ZeroRequest): Response => {
  return Response.json({
    params: req.params,
    query: req.query,
    context: req.ctx,
  })
}

🏆 Production-Ready Features

0http-bun is trusted by developers for production workloads thanks to its comprehensive feature set:

📦 Comprehensive Middleware Ecosystem

  • Body Parser: JSON, URL-encoded, multipart, and text parsing with security
  • Authentication: JWT and API key authentication with flexible validation
  • CORS: Cross-origin resource sharing with dynamic origin support
  • Rate Limiting: Sliding window rate limiting with memory-efficient storage
  • Logging: Structured logging with Pino integration and request tracing
  • Metrics: Prometheus metrics export for monitoring and alerting

🔧 Developer Tools

  • TypeScript Support: Full type definitions and IntelliSense
  • Error Handling: Comprehensive error management with custom handlers; async errors caught automatically
  • Request Context: Flexible context system for middleware data sharing
  • Parameter Parsing: Automatic URL parameter and query string parsing
  • Route Caching: LRU-style caching with configurable cacheSize for bounded memory usage

🚀 Deployment Ready

  • Environment Agnostic: Works with any Bun deployment platform
  • Minimal Dependencies: Small attack surface with carefully selected dependencies
  • Memory Efficient: Optimized for serverless and containerized deployments
  • Scalable Architecture: Designed for horizontal scaling and load balancing

📈 Benchmarks & Comparisons

0http-bun consistently outperforms other frameworks in Bun environments:

Framework Requests/sec Memory Usage Latency (p95)
0http-bun ~85,000 ~45MB ~2.1ms
Express ~42,000 ~68MB ~4.8ms
Fastify ~78,000 ~52MB ~2.4ms
Hono ~82,000 ~48MB ~2.2ms

Benchmarks run on Bun v1.2.2 with simple JSON response routes. Results may vary based on hardware and configuration.

🌟 Community & Support

  • 📚 Comprehensive Documentation: Detailed guides and API reference
  • 🔧 Active Development: Regular updates and feature improvements
  • 🐛 Issue Tracking: Responsive bug reports and feature requests
  • 💬 Community Discussions: GitHub Discussions for questions and ideas
  • 🎯 Production Proven: Used in production by companies worldwide

Changelog

v1.3.0 — Security Hardening Release

This release addresses 43 vulnerabilities (6 Critical, 13 High, 13 Medium, 7 Low, 4 Info) identified in a comprehensive penetration test. All 43 issues have been resolved.

Breaking Changes

Change Old Behavior New Behavior
Rate limit key generator Trusted X-Forwarded-For, X-Real-IP, CF-Connecting-IP proxy headers Uses req.ip || req.remoteAddress || 'unknown' only. Supply a custom keyGenerator if behind a reverse proxy.
JWT default algorithms ['HS256', 'RS256'] ['HS256'] only. Mixed symmetric + asymmetric algorithms throw an error. Explicitly set algorithms: ['RS256'] if using RSA keys.
Default error handler Returned err.message to the client in the response body Returns generic "Internal Server Error". Full error logged server-side via console.error.
parseLimit validation Silently returned 1MB default for false, null, objects Throws TypeError for unexpected types.
JWT token in context req.jwt.token and req.ctx.jwt.token contained the raw JWT string Raw token no longer stored on the request context. Only payload and header are available.
JWT module exports Exported internal functions (extractToken, handleAuthError, extractTokenFromHeader, etc.) Only exports createJWTAuth, createAPIKeyAuth, and API_KEY_SYMBOL. Internal helpers are no longer exposed.
Rate limit store override req.rateLimitStore could override the configured store at runtime Always uses the constructor-configured store. req.rateLimitStore has no effect.
API key in context req.apiKey / req.ctx.apiKey contained the raw API key Now stores a masked version (xxxx****xxxx). Raw key available via req[API_KEY_SYMBOL].
Empty JSON body Empty/whitespace JSON body silently returned {} Now sets req.body to undefined. Applications must handle undefined explicitly.

Core Router

  • LRU route cache with configurable cacheSize (default: 1000) — prevents unbounded memory growth from cache poisoning attacks.
  • URL path normalization — double slashes collapsed, URI-decoded (preserving %2F), preventing route filter bypass via //admin or %2e%2e paths.
  • Immutable empty paramsObject.freeze({}) prevents cross-request data leakage on parameterless routes.
  • router.use() return fixreturn this in arrow function corrected to return router for proper chaining.
  • Query prototype pollution protection — dangerous keys (__proto__, constructor, prototype) are filtered from parsed query strings, mirroring existing route params protection.

Middleware Chain

  • Async error handling — rejected promises from async middleware are now caught via .catch() and forwarded to the errorHandler. Previously, they were unhandled.

Body Parser

  • String-aware JSON nesting depth scanner — brace characters inside JSON strings no longer count toward nesting depth, fixing a bypass where {"key": "{{{..."} could evade the depth check.
  • Custom jsonParser enforces size limits — size validation runs before the custom parser function is called.
  • Prototype pollution protection for multipart — uses Object.create(null) for body/files objects and blocklists dangerous property names.
  • Multipart filename sanitization — strips .., path separators (/, \), null bytes, and leading dots. The original filename is preserved in file.originalName.
  • Strict content-type matching — JSON parser matches application/json only (was application/ which matched application/xml, application/octet-stream, etc.).
  • parseLimit type validation — throws TypeError on unexpected input types instead of silently defaulting.
  • Empty JSON body handling — empty or whitespace-only JSON bodies now set req.body to undefined instead of silently returning {}. (Breaking change)
  • Raw body via Symbol — raw body text is now stored via Symbol.for('0http.rawBody') (RAW_BODY_SYMBOL) instead of req._rawBodyText, preventing accidental serialization/logging. The symbol is exported from the body parser module.

JWT Authentication

  • Timing-safe API key comparison — all API key comparisons use crypto.timingSafeEqual() with constant-time length mismatch handling.
  • Algorithm confusion prevention — throws if both symmetric (HS*) and asymmetric (RS*/ES*/PS*) algorithms are configured.
  • Secure path exclusion — exact match or path + '/' boundary checking replaces prefix matching (prevents /healthcheck bypassing /health exclusion).
  • Optional mode transparency — when optional: true and a token is invalid, sets req.ctx.authError and req.ctx.authAttempted = true instead of silently proceeding.
  • Unified error messages — JWT signature/expiry/claim validation failures return "Invalid or expired token" to prevent oracle attacks. Distinct messages are used for missing tokens and API key failures.
  • Safe option merging...jwtOptions spread applied first; security-critical options (algorithms, audience, issuer) override after.
  • Validator call signature — always calls apiKeyValidator(apiKey, req) regardless of Function.length arity.
  • Reduced export surface — only createJWTAuth and createAPIKeyAuth are exported.
  • Token type validation — new requiredTokenType option validates the JWT typ header claim (case-insensitive). Rejects tokens with missing or incorrect type when configured.
  • API key via Symbol — raw API key available via req[API_KEY_SYMBOL] (exported Symbol.for('0http.apiKey')). req.apiKey and req.ctx.apiKey now store a masked version (xxxx****xxxx). (Breaking change)
  • Error logging in auth handler — empty catch blocks in handleAuthError now log errors via console.error for debugging visibility.

Rate Limiting

  • Secure default key generator — uses req.ip || req.remoteAddress || 'unknown' instead of trusting proxy headers.
  • Sliding window memory boundsmaxKeys option (default: 10,000) with periodic cleanup via setInterval + unref().
  • Synchronous MemoryStore.increment — eliminates TOCTOU race condition in the fixed-window store.
  • Exact path exclusionexcludePaths uses exact or boundary matching.
  • Configurable header disclosurestandardHeaders now accepts true (full headers, default), false (no headers), or 'minimal' (only Retry-After on 429 responses) to control rate limit information disclosure.
  • Unique unknown keys — when no IP is available, the default key generator now creates unique per-request keys instead of sharing a single 'unknown' bucket, preventing shared-bucket DoS.

CORS

  • Null origin rejectionnull/missing origins rejected before calling validator functions or checking arrays, preventing sandboxed iframe bypass.
  • Conditional CORS headers — headers only set when the origin is actually allowed (previously leaked method/header lists even on rejected origins).
  • Vary: Origin — set for all non-wildcard origins (previously only set for function/array origins).
  • Single allowedHeaders resolutionallowedHeaders function is now resolved once per preflight request instead of multiple times, preventing inconsistency.

v1.2.2

  • Middleware dependencies (jose, pino, prom-client) made optional with lazy loading.
  • Prometheus metrics middleware added.
  • Logger middleware added.

Migrating to v1.3.0

Rate Limiting Behind a Reverse Proxy

The default keyGenerator no longer reads proxy headers. If your application runs behind a reverse proxy (nginx, Cloudflare, AWS ALB, etc.), you must provide a custom keyGenerator:

router.use(
  createRateLimit({
    windowMs: 15 * 60 * 1000,
    max: 100,
    keyGenerator: (req) => {
      // Trust the header your reverse proxy sets
      return (
        req.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ||
        req.ip ||
        'unknown'
      )
    },
  }),
)

JWT Algorithm Configuration

If you were relying on the default ['HS256', 'RS256'] algorithm list, you must now be explicit:

// For HMAC (symmetric) secrets:
createJWTAuth({
  secret: process.env.JWT_SECRET,
  algorithms: ['HS256'], // This is now the default
})

// For RSA (asymmetric) keys:
createJWTAuth({
  jwksUri: 'https://your-idp.com/.well-known/jwks.json',
  algorithms: ['RS256'], // Must be explicit — mixing with HS256 will throw
})

Error Handler

If you relied on err.message being returned to clients from the default error handler, provide a custom errorHandler:

const {router} = http({
  errorHandler: (err: Error) => {
    // Old behavior (NOT recommended for production):
    return new Response(err.message, {status: 500})
  },
})

parseLimit Validation

If your code passes non-string/non-number values to parseLimit (e.g., false, null, objects), update it to pass a valid value:

// Before (silently defaulted to 1MB):
createBodyParser({json: {limit: someConfig.limit}}) // someConfig.limit might be null

// After (throws TypeError):
createBodyParser({json: {limit: someConfig.limit || '1mb'}}) // Provide a fallback

JWT Token Context

If you accessed the raw JWT token string via req.jwt.token or req.ctx.jwt.token, note that only payload and header are now available:

// Before:
const rawToken = req.jwt.token // No longer available

// After:
const payload = req.jwt.payload // Decoded payload
const header = req.jwt.header // Protected header

API Key Access

If you accessed the raw API key via req.apiKey or req.ctx.apiKey, note that these now contain a masked version. Use the exported API_KEY_SYMBOL for programmatic access:

import {API_KEY_SYMBOL} from '0http-bun/lib/middleware/jwt-auth'

// Before:
const rawKey = req.apiKey // Was the raw API key, now masked (xxxx****xxxx)

// After:
const rawKey = req[API_KEY_SYMBOL] // Symbol.for('0http.apiKey')
const maskedKey = req.apiKey // 'xxxx****xxxx' (safe for logging)

Empty JSON Body

If your code relied on empty JSON request bodies being parsed as {}, update it to handle undefined:

// Before (empty body → {}):
router.post('/api/data', (req) => {
  const keys = Object.keys(req.body) // Always worked
})

// After (empty body → undefined):
router.post('/api/data', (req) => {
  const body = req.body || {}
  const keys = Object.keys(body) // Handle undefined
})

Raw Body Access

If you accessed raw body text via req._rawBodyText, use the exported RAW_BODY_SYMBOL instead:

import {RAW_BODY_SYMBOL} from '0http-bun/lib/middleware/body-parser'

// Before:
const rawBody = req._rawBodyText // Public string property

// After:
const rawBody = req[RAW_BODY_SYMBOL] // Symbol.for('0http.rawBody')

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

Development Setup

# Clone the repository
git clone https://github.com/BackendStack21/0http-bun.git
cd 0http-bun

# Install dependencies
bun install

# Run tests
bun test

# Run benchmarks
bun run bench

# Format code
bun run format

Related Projects

  • 0http - The original inspiration
  • Bun - The JavaScript runtime this framework is built for
  • Trouter - Fast routing library used under the hood
  • Fast QueryString - Optimized query string parsing

About

Bun-based HTTP framework inspired by 0http

Resources

License

Stars

Watchers

Forks

Sponsor this project

Packages

No packages published