Complete API documentation for Bungate.
import { BunGateway } from 'bungate'
const gateway = new BunGateway(config: GatewayConfig)Add a route to the gateway.
gateway.addRoute({
pattern: '/api/*',
target: 'http://backend:3000',
})Start the gateway server.
await gateway.listen()
await gateway.listen(3000) // Override portGracefully shutdown the gateway.
await gateway.close()Get the health status of all load balancer targets.
const targets = gateway.getTargetStatus()
targets.forEach((target) => {
console.log(`${target.url}: ${target.healthy ? '✓' : '✗'}`)
})Complete configuration interface:
interface GatewayConfig {
server?: ServerConfig
cluster?: ClusterConfig
security?: SecurityConfig
auth?: AuthConfig
cors?: CorsConfig
metrics?: MetricsConfig
logger?: LoggerInterface
}interface ServerConfig {
port?: number // Default: 3000
hostname?: string // Default: '0.0.0.0'
development?: boolean // Default: false
}Example:
const gateway = new BunGateway({
server: {
port: 8080,
hostname: 'localhost',
development: true,
},
})interface ClusterConfig {
enabled: boolean // Enable cluster mode
workers?: number // Default: CPU cores
restartWorkers?: boolean // Default: true
restartDelay?: number // Default: 1000ms
maxRestarts?: number // Default: 10
respawnThreshold?: number // Default: 5
respawnThresholdTime?: number // Default: 60000ms
shutdownTimeout?: number // Default: 30000ms
exitOnShutdown?: boolean // Default: true
allowedEnvVars?: string[] // Allow-list of env vars forwarded to workers
workerScriptAllowlist?: string[] // Allow-list of worker script paths
}Example:
const gateway = new BunGateway({
cluster: {
enabled: true,
workers: 4,
restartWorkers: true,
maxRestarts: 10,
shutdownTimeout: 30000,
},
})interface SecurityConfig {
tls?: TLSConfig
securityHeaders?: SecurityHeadersConfig
inputValidation?: InputValidationConfig
sizeLimits?: SizeLimitsConfig
trustedProxies?: TrustedProxiesConfig
jwtKeyRotation?: JWTKeyRotationConfig
}interface TLSConfig {
enabled: boolean
cert: string | Buffer
key: string | Buffer
ca?: string | Buffer
minVersion?: 'TLSv1.2' | 'TLSv1.3'
cipherSuites?: string[]
redirectHTTP?: boolean
redirectPort?: number
/** Allowed Host header values for the HTTP->HTTPS redirect server. Supports leading wildcards. */
redirectAllowedHosts?: string[]
}Example:
const gateway = new BunGateway({
security: {
tls: {
enabled: true,
cert: './cert.pem',
key: './key.pem',
minVersion: 'TLSv1.3',
redirectHTTP: true,
redirectPort: 80,
redirectAllowedHosts: ['example.com', '*.example.com'],
},
},
})interface TrustedProxiesConfig {
enabled: boolean
trustedIPs?: string[]
trustedNetworks?: string[]
maxForwardedDepth?: number
trustAll?: boolean
/** Honor CF-Connecting-IP only when the immediate proxy is Cloudflare. Default: false */
trustCloudflare?: boolean
/** Honor X-Real-IP / X-Client-IP from the validated immediate proxy. Default: false */
trustXRealIP?: boolean
}interface SecurityHeadersConfig {
enabled: boolean
hsts?: {
maxAge: number
includeSubDomains?: boolean
preload?: boolean
}
contentSecurityPolicy?: {
directives: Record<string, string[]>
}
xFrameOptions?: 'DENY' | 'SAMEORIGIN'
xContentTypeOptions?: boolean
}Example:
security: {
securityHeaders: {
enabled: true,
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true,
},
contentSecurityPolicy: {
directives: {
'default-src': ["'self'"],
'script-src': ["'self'", "'unsafe-inline'"],
},
},
xFrameOptions: 'DENY',
xContentTypeOptions: true,
},
}interface SizeLimitsConfig {
maxBodySize?: number // Default: 10MB
maxHeaderSize?: number // Default: 16KB
maxUrlLength?: number // Default: 2048
}Example:
security: {
sizeLimits: {
maxBodySize: 50 * 1024 * 1024, // 50 MB
maxHeaderSize: 32 * 1024, // 32 KB
maxUrlLength: 4096,
},
}interface AuthConfig {
/** Symmetric secret, PEM string, imported key, or a resolver function */
secret?:
| string
| Uint8Array
| JWTKeyLike
| ((req: Request) => JWTKeyLike | Promise<JWTKeyLike>)
jwksUri?: string
jwks?: any
jwtOptions?: Record<string, any>
/** Required issuer claim (recommended) */
issuer?: string | string[]
/** Required audience claim (recommended) */
audience?: string | string[]
/** Explicitly allowed algorithms. Derived from the key type when omitted. */
algorithms?: string[]
apiKeys?:
| string[]
| ((
apiKey: string,
req: Request,
) => boolean | object | Promise<boolean | object>)
apiKeyHeader?: string
apiKeyValidator?: (
key: string,
req: Request,
) => boolean | object | Promise<boolean | object>
/** Boundary-aware path exclusions. `/public/*` excludes `/public/foo` but not `/publicity/foo */
excludePaths?: string[]
optional?: boolean
getToken?: (req: Request) => string | undefined | Promise<string | undefined>
tokenHeader?: string
tokenQuery?: string
unauthorizedResponse?:
| Response
| ((error: Error, req: Request) => Response | object)
onError?: (
error: Error,
req: Request,
) => Response | void | Promise<Response | void>
}Example:
const gateway = new BunGateway({
auth: {
secret: process.env.JWT_SECRET, // HS256 secrets should be >= 32 bytes
issuer: 'https://auth.example.com',
audience: 'https://api.example.com',
// algorithms is optional; Bungate derives it from the key type.
excludePaths: ['/health', '/public/*'],
},
})interface CorsConfig {
origin: string | string[] | boolean
methods?: string[]
allowedHeaders?: string[]
exposedHeaders?: string[]
credentials?: boolean
maxAge?: number
}Example:
const gateway = new BunGateway({
cors: {
origin: ['https://app.example.com', 'https://admin.example.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
credentials: true,
maxAge: 86400,
},
})interface MetricsConfig {
enabled: boolean
path?: string // Default: '/metrics'
}Example:
const gateway = new BunGateway({
metrics: {
enabled: true,
path: '/metrics',
},
})interface RouteConfig {
pattern: string
target?: string
loadBalancer?: LoadBalancerConfig
handler?: (req: Request) => Promise<Response> | Response
auth?: AuthConfig
rateLimit?: RateLimitConfig
circuitBreaker?: CircuitBreakerConfig
timeout?: number
middlewares?: Middleware[]
proxy?: GatewayProxyOptions
hooks?: RouteHooks
}interface LoadBalancerConfig {
strategy:
| 'round-robin'
| 'least-connections'
| 'weighted'
| 'ip-hash'
| 'random'
| 'p2c'
| 'latency'
| 'weighted-least-connections'
targets: TargetConfig[]
healthCheck?: HealthCheckConfig
stickySession?: StickySessionConfig
}interface TargetConfig {
url: string
weight?: number // For weighted strategies
}Example:
gateway.addRoute({
pattern: '/api/*',
loadBalancer: {
strategy: 'weighted',
targets: [
{ url: 'http://api1.example.com', weight: 70 },
{ url: 'http://api2.example.com', weight: 30 },
],
},
})interface HealthCheckConfig {
enabled: boolean
interval?: number // Default: 30000ms, clamped to 1000ms–300000ms
timeout?: number // Default: 5000ms, must be less than interval
path?: string // Default: '/health'
method?: 'GET' | 'HEAD' // Default: 'GET'
expectedStatus?: number // Default: 200
expectedBody?: string // Optional body substring required for healthy status
failureThreshold?: number // Default: 3, max 20
successThreshold?: number // Default: 2, max 20
minHealthyTargets?: number // Default: 1
allowedSchemes?: string[] // Default: ['http', 'https']
allowedHosts?: string[] // CIDR or exact hosts allowed for health checks
validator?: (response: Response) => Promise<boolean> | boolean
}Example:
loadBalancer: {
strategy: 'least-connections',
targets: [/* ... */],
healthCheck: {
enabled: true,
interval: 15000,
timeout: 5000,
path: '/health',
expectedStatus: 200,
validator: async (response) => {
if (response.status !== 200) return false
const data = await response.json()
return data.status === 'healthy'
},
},
}interface StickySessionConfig {
enabled: boolean
cookieName?: string // Default: 'bungate_session'
ttl?: number // Default: 3600000ms (1 hour)
secure?: boolean // Default: false
httpOnly?: boolean // Default: true
sameSite?: 'strict' | 'lax' | 'none'
/** Maximum in-memory sessions. Oldest entries are evicted when the cap is hit. */
maxSessions?: number // Default: 10000
/**
* How to handle unknown session cookies.
* 'ignore' treats them as cookie-less (prevents memory DoS).
* 'create' mints a new session for the unknown value.
*/
unknownCookiePolicy?: 'ignore' | 'create' // Default: 'ignore'
}Example:
loadBalancer: {
strategy: 'least-connections',
targets: [/* ... */],
stickySession: {
enabled: true,
cookieName: 'app_session',
ttl: 7200000, // 2 hours
secure: true,
httpOnly: true,
sameSite: 'lax',
},
}interface RateLimitConfig {
max: number // Max requests
windowMs: number // Time window in ms
keyGenerator?: (req: Request) => string
message?: string
statusCode?: number
/** Boundary-aware path exclusions for rate limiting */
excludePaths?: string[]
}Example:
gateway.addRoute({
pattern: '/api/*',
target: 'http://api:3000',
rateLimit: {
max: 100,
windowMs: 60000, // 1 minute
keyGenerator: (req) => {
return (
req.headers.get('x-api-key') ||
req.headers.get('x-forwarded-for') ||
'unknown'
)
},
message: 'Too many requests',
statusCode: 429,
},
})interface CircuitBreakerConfig {
enabled: boolean
failureThreshold?: number // Default: 5
timeout?: number // Default: 10000ms
resetTimeout?: number // Default: 30000ms
halfOpenRequests?: number // Default: 3
}Example:
gateway.addRoute({
pattern: '/api/*',
target: 'http://api:3000',
circuitBreaker: {
enabled: true,
failureThreshold: 5,
timeout: 10000,
resetTimeout: 30000,
},
hooks: {
onError: async (req, error) => {
return new Response(JSON.stringify({ error: 'Service unavailable' }), {
status: 503,
})
},
},
})interface GatewayProxyOptions {
timeout?: number
headers?: Record<string, string | (() => string)>
stripPath?: boolean
preserveHostHeader?: boolean
/** Rewrite incoming paths before forwarding (regex -> replacement, or function) */
pathRewrite?: Record<string, string> | ((path: string) => string)
/** Allowed redirect target hostnames when followRedirects is enabled. Supports leading wildcards. */
redirectAllowlist?: string[]
/** Allow redirects only to the same origin as the original upstream target. Default: true */
redirectSameOrigin?: boolean
/** Per-request fetch RequestInit options (used internally for SSRF protection) */
request?: RequestInit
}Example:
gateway.addRoute({
pattern: '/api/*',
target: 'http://api:3000',
proxy: {
timeout: 30000,
headers: {
'X-Gateway-Version': '1.0.0',
'X-Request-ID': () => crypto.randomUUID(),
},
stripPath: false,
preserveHostHeader: true,
// Redirects are followed manually only for same-origin or allow-listed hosts.
redirectSameOrigin: true,
},
})interface RouteHooks {
onRequest?: (req: Request) => Promise<Request | Response> | Request | Response
onResponse?: (req: Request, res: Response) => Promise<Response> | Response
onError?: (req: Request, error: Error) => Promise<Response> | Response
}Example:
gateway.addRoute({
pattern: '/api/*',
target: 'http://api:3000',
hooks: {
onRequest: async (req) => {
console.log('Request:', req.method, req.url)
return req
},
onResponse: async (req, res) => {
console.log('Response:', res.status)
return res
},
onError: async (req, error) => {
console.error('Error:', error)
return new Response('Internal Server Error', { status: 500 })
},
},
})type Middleware = (
req: Request,
next: () => Promise<Response>,
) => Promise<Response> | Response// Logging middleware
const loggingMiddleware: Middleware = async (req, next) => {
const start = Date.now()
console.log('→', req.method, req.url)
const response = await next()
const duration = Date.now() - start
console.log('←', response.status, `(${duration}ms)`)
return response
}
// Authentication middleware
const authMiddleware: Middleware = async (req, next) => {
const token = req.headers.get('authorization')
if (!token) {
return new Response('Unauthorized', { status: 401 })
}
// Validate token...
return next()
}
// Use middleware
gateway.addRoute({
pattern: '/api/*',
target: 'http://api:3000',
middlewares: [loggingMiddleware, authMiddleware],
})import { PinoLogger } from 'bungate'
const logger = new PinoLogger(config: LoggerConfig)interface LoggerConfig {
level?: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal'
prettyPrint?: boolean
enableRequestLogging?: boolean
}Example:
import { BunGateway, PinoLogger } from 'bungate'
const logger = new PinoLogger({
level: 'info',
prettyPrint: true,
enableRequestLogging: true,
})
const gateway = new BunGateway({
server: { port: 3000 },
logger,
})logger.trace(obj, msg?)
logger.debug(obj, msg?)
logger.info(obj, msg?)
logger.warn(obj, msg?)
logger.error(obj, msg?)
logger.fatal(obj, msg?)Example:
logger.info({ userId: 123 }, 'User logged in')
logger.error({ error: err }, 'Request failed')
logger.debug({ request: req }, 'Processing request')// Target status
interface TargetStatus {
url: string
healthy: boolean
connections: number
latency?: number
}
// Request with user
interface AuthenticatedRequest extends Request {
user?: {
id: string
email?: string
[key: string]: any
}
}
// Error response
interface ErrorResponse {
error: string
message?: string
statusCode: number
}// Check if request is authenticated
function isAuthenticated(req: Request): req is AuthenticatedRequest {
return 'user' in req
}
// Use in middleware
const middleware: Middleware = async (req, next) => {
if (isAuthenticated(req)) {
console.log('User:', req.user.id)
}
return next()
}- Quick Start - Get started with Bungate
- Authentication - Auth configuration
- Load Balancing - Load balancing strategies
- Clustering - Multi-process scaling
- Security - Security features
- TLS Configuration - HTTPS setup
- Troubleshooting - Common issues
Need more examples? Check the examples directory.