Bun.serve Integration

hitlimit provides direct integration with Bun's native HTTP server for maximum performance. This guide covers patterns for using rate limiting with Bun.serve.

Basic Integration

Add rate limiting to any Bun.serve application:

server.ts
import { hitlimit } from '@joint-ops/hitlimit-bun'

const limiter = hitlimit({ limit: 100, window: '1m' })

Bun.serve({
  port: 3000,
  async fetch(req) {
    const { allowed, remaining, reset, limit } = await limiter.check(req)

    const headers = {
      'X-RateLimit-Limit': String(limit),
      'X-RateLimit-Remaining': String(remaining),
      'X-RateLimit-Reset': String(reset)
    }

    if (!allowed) {
      return new Response('Rate limit exceeded', {
        status: 429,
        headers: { ...headers, 'Retry-After': String(reset) }
      })
    }

    return new Response('Hello World!', { headers })
  }
})

Route-Based Rate Limiting

Apply different limits to different routes:

routes.ts
import { hitlimit } from '@joint-ops/hitlimit-bun'

// Different limiters for different endpoints
const apiLimiter = hitlimit({ limit: 100, window: '1m' })
const authLimiter = hitlimit({ limit: 5, window: '15m' })
const uploadLimiter = hitlimit({ limit: 10, window: '1h' })

Bun.serve({
  port: 3000,
  async fetch(req) {
    const url = new URL(req.url)

    // Select limiter based on route
    let limiter = apiLimiter
    if (url.pathname.startsWith('/auth')) {
      limiter = authLimiter
    } else if (url.pathname.startsWith('/upload')) {
      limiter = uploadLimiter
    }

    const result = await limiter.check(req)

    if (!result.allowed) {
      return new Response('Rate limited', { status: 429 })
    }

    // Route handling...
    return new Response('OK')
  }
})

IP Address Extraction

Extract client IP from various headers:

ip-extraction.ts
import { hitlimit } from '@joint-ops/hitlimit-bun'

const limiter = hitlimit({
  limit: 100,
  window: '1m',
  key(req, server) {
    // Check for proxy headers first
    const forwarded = req.headers.get('X-Forwarded-For')
    if (forwarded) {
      return forwarded.split(',')[0].trim()
    }

    // Cloudflare
    const cfIP = req.headers.get('CF-Connecting-IP')
    if (cfIP) return cfIP

    // Fallback to Bun's socket address
    return server.requestIP(req)?.address || 'unknown'
  }
})

WebSocket Support

Rate limit WebSocket connections:

websocket.ts
import { hitlimit } from '@joint-ops/hitlimit-bun'

const wsLimiter = hitlimit({ limit: 10, window: '1m' })

Bun.serve({
  port: 3000,
  async fetch(req, server) {
    if (req.headers.get('upgrade') === 'websocket') {
      const result = await wsLimiter.check(req)

      if (!result.allowed) {
        return new Response('Too many connections', { status: 429 })
      }

      server.upgrade(req)
      return
    }

    return new Response('Use WebSocket')
  },
  websocket: {
    message(ws, msg) {
      ws.send(`Echo: ${msg}`)
    }
  }
})

Error Handling

Handle rate limiter errors gracefully:

async fetch(req) {
  try {
    const result = await limiter.check(req)
    if (!result.allowed) {
      return new Response('Rate limited', { status: 429 })
    }
  } catch (error) {
    // Log error but allow request (fail-open)
    console.error('Rate limiter error:', error)
  }

  return new Response('OK')
}

Next Steps