Tools
Tools: The Complete Guide to @hazeljs/gateway: Intelligent API Gateway for Microservices
2026-02-26
0 views
admin
Table of Contents ## Introduction ## Why an API Gateway? ## Architecture Overview ## High-Level Flow ## Request Lifecycle ## Key Components ## Core Concepts ## Route Matching ## Path Rewriting ## Service Discovery Integration ## Getting Started ## 1. Install Dependencies ## 2. Minimal Config-Driven Gateway ## 3. Run the Demos ## Version Routing ## Strategy: Header ## Strategy: URI Prefix ## Strategy: Query Parameter ## Strategy: Weighted ## Canary Deployments ## How It Works ## Configuration ## Promotion Strategies ## Events ## Manual Control ## Resilience Patterns ## Circuit Breaker ## Rate Limiting ## Retry & Timeout ## Traffic Management ## Traffic Mirroring ## Request/Response Transforms ## Production Integration ## Gateway with HazelApp ## Config from Environment Variables ## Best Practices ## 1. Use Config-Driven Routes in Production ## 2. Start with Conservative Canary Weights ## 3. Set Appropriate Evaluation Windows ## 4. Monitor Canary Events ## 5. Circuit Breakers on Every Route ## 6. Per-Route Rate Limits ## 7. Traffic Mirroring Before Canary ## 8. Keep the Gateway Stateless ## Summary ## Further Reading A practical, in-depth exploration of the HazelJS API Gateway — from routing and versioning to canary deployments and production-ready patterns. The @hazeljs/gateway package is an intelligent API gateway designed for HazelJS microservices. It sits between your clients and backend services, providing: This guide walks you through each feature with practical examples and diagrams. In a microservices architecture, clients would otherwise need to know about every service: With a gateway, clients have a single entry point: The gateway supports flexible path patterns: Routes are sorted by specificity — more specific patterns match first: The gateway can transform paths before forwarding: Example: GET /api/users/123 → forwarded as GET /users/123 to user-service. The gateway uses @hazeljs/discovery to find backend instances. It supports: Load balancing strategies: round-robin, random, least-connections, weighted-round-robin, IP hash, zone-aware. From the hazeljs-gateway-starter directory: Version routing lets you serve multiple API versions from the same gateway path. Clients opt-in by sending X-API-Version: v2: RESTful versioning: /v1/api/users, /v2/api/users Quick testing: ?version=v2 A/B testing: 80% to v1, 20% to v2 (random distribution) Canary deployments gradually shift traffic from a stable version to a new version. The gateway monitors error rates and latency, then automatically promotes or rolls back. The gateway uses @hazeljs/resilience for per-route protection. Prevents cascading failures by opening the circuit when too many requests fail: Returns 429 Too Many Requests with Retry-After header when exceeded: Strategies: token-bucket (allows bursts) or sliding-window (rolling limit). Send a percentage of traffic to a shadow service without affecting the response: Use cases: Testing new versions with real traffic, comparing responses, performance benchmarking. Modify headers or body in transit (see gateway docs for transform API). The recommended production setup: run the gateway behind HazelApp's HTTP server. This gives you: Flow: Requests to /api/* go to the gateway; other paths (e.g. /health) go to HazelApp controllers. For 12-factor apps, drive config from env vars: Decorators are great for prototyping; config-driven routes let you change behavior without redeploying. Begin with 5–10% canary traffic. Increase via env vars as confidence grows. Always log canary:promote, canary:rollback, and canary:complete to your monitoring system. Even if you trust downstream services, circuit breakers prevent cascading failures during outages. Different services have different capacity. Set limits based on each service's throughput. Before running a canary, mirror traffic to the new version to compare responses and catch obvious bugs. All state (canary weights, circuit breaker state) is in-memory. On restart, the gateway rebuilds from config. The @hazeljs/gateway package integrates seamlessly with @hazeljs/discovery and @hazeljs/resilience to provide a production-ready API gateway for your HazelJS microservices. Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse CODE_BLOCK:
Without Gateway:
┌─────────┐ ┌─────────────────┐
│ Client │────▶│ user-service │ :3001
└─────────┘ └─────────────────┘ │ │ ┌─────────────────┐ ├───────▶│ order-service │ :3002 │ └─────────────────┘ │ │ ┌─────────────────┐ └───────▶│ payment-service │ :3003 └─────────────────┘ Problems: Multiple URLs, CORS, auth per service, no central control Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
Without Gateway:
┌─────────┐ ┌─────────────────┐
│ Client │────▶│ user-service │ :3001
└─────────┘ └─────────────────┘ │ │ ┌─────────────────┐ ├───────▶│ order-service │ :3002 │ └─────────────────┘ │ │ ┌─────────────────┐ └───────▶│ payment-service │ :3003 └─────────────────┘ Problems: Multiple URLs, CORS, auth per service, no central control CODE_BLOCK:
Without Gateway:
┌─────────┐ ┌─────────────────┐
│ Client │────▶│ user-service │ :3001
└─────────┘ └─────────────────┘ │ │ ┌─────────────────┐ ├───────▶│ order-service │ :3002 │ └─────────────────┘ │ │ ┌─────────────────┐ └───────▶│ payment-service │ :3003 └─────────────────┘ Problems: Multiple URLs, CORS, auth per service, no central control CODE_BLOCK:
With Gateway:
┌─────────┐ ┌──────────────────────────────────────────┐
│ Client │────▶│ API Gateway │
└─────────┘ │ /api/users → user-service │ │ /api/orders → order-service │ │ /api/payments→ payment-service │ └──────────────────────────────────────────┘ │ ┌───────────────┼───────────────┐ ▼ ▼ ▼ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ user-svc │ │ order-svc │ │ payment-svc │ └─────────────┘ └─────────────┘ └─────────────┘ Benefits: Single URL, central auth, routing, resilience, metrics Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
With Gateway:
┌─────────┐ ┌──────────────────────────────────────────┐
│ Client │────▶│ API Gateway │
└─────────┘ │ /api/users → user-service │ │ /api/orders → order-service │ │ /api/payments→ payment-service │ └──────────────────────────────────────────┘ │ ┌───────────────┼───────────────┐ ▼ ▼ ▼ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ user-svc │ │ order-svc │ │ payment-svc │ └─────────────┘ └─────────────┘ └─────────────┘ Benefits: Single URL, central auth, routing, resilience, metrics CODE_BLOCK:
With Gateway:
┌─────────┐ ┌──────────────────────────────────────────┐
│ Client │────▶│ API Gateway │
└─────────┘ │ /api/users → user-service │ │ /api/orders → order-service │ │ /api/payments→ payment-service │ └──────────────────────────────────────────┘ │ ┌───────────────┼───────────────┐ ▼ ▼ ▼ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ user-svc │ │ order-svc │ │ payment-svc │ └─────────────┘ └─────────────┘ └─────────────┘ Benefits: Single URL, central auth, routing, resilience, metrics CODE_BLOCK:
┌─────────────────┐ │ HTTP Client │ └────────┬────────┘ │ GET /api/users ▼
┌────────────────────────────────────────────────────────────────────────────┐
│ GATEWAY LAYER │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌─────────────┐ │
│ │Route Matcher │──▶│Version Router│──▶│Canary Engine │──▶│ServiceProxy │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────┬──────┘ │
│ │ │
│ ┌──────────────┐ ┌──────────────┐ │ │
│ │CircuitBreaker│◀──│ Rate Limiter │◀────────────────────────────┘ │
│ └──────────────┘ └──────────────┘ │
└────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────┐ │ DiscoveryClient │ └────────┬────────┘ │ Load balanced ┌──────────────┼──────────────┐ ▼ ▼ ▼ ┌───────────┐ ┌───────────┐ ┌───────────┐ │user-svc v1│ │user-svc v2│ │order-svc │ └───────────┘ └───────────┘ └───────────┘ Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
┌─────────────────┐ │ HTTP Client │ └────────┬────────┘ │ GET /api/users ▼
┌────────────────────────────────────────────────────────────────────────────┐
│ GATEWAY LAYER │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌─────────────┐ │
│ │Route Matcher │──▶│Version Router│──▶│Canary Engine │──▶│ServiceProxy │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────┬──────┘ │
│ │ │
│ ┌──────────────┐ ┌──────────────┐ │ │
│ │CircuitBreaker│◀──│ Rate Limiter │◀────────────────────────────┘ │
│ └──────────────┘ └──────────────┘ │
└────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────┐ │ DiscoveryClient │ └────────┬────────┘ │ Load balanced ┌──────────────┼──────────────┐ ▼ ▼ ▼ ┌───────────┐ ┌───────────┐ ┌───────────┐ │user-svc v1│ │user-svc v2│ │order-svc │ └───────────┘ └───────────┘ └───────────┘ CODE_BLOCK:
┌─────────────────┐ │ HTTP Client │ └────────┬────────┘ │ GET /api/users ▼
┌────────────────────────────────────────────────────────────────────────────┐
│ GATEWAY LAYER │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌─────────────┐ │
│ │Route Matcher │──▶│Version Router│──▶│Canary Engine │──▶│ServiceProxy │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────┬──────┘ │
│ │ │
│ ┌──────────────┐ ┌──────────────┐ │ │
│ │CircuitBreaker│◀──│ Rate Limiter │◀────────────────────────────┘ │
│ └──────────────┘ └──────────────┘ │
└────────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────┐ │ DiscoveryClient │ └────────┬────────┘ │ Load balanced ┌──────────────┼──────────────┐ ▼ ▼ ▼ ┌───────────┐ ┌───────────┐ ┌───────────┐ │user-svc v1│ │user-svc v2│ │order-svc │ └───────────┘ └───────────┘ └───────────┘ CODE_BLOCK:
Request: Client ──▶ Gateway ──▶ Route Match ──▶ Version Select ──▶ Canary Engine │ ▼ Service Proxy (circuit breaker + rate limit) ──▶ Discovery ──▶ Backend Response: Client ◀── Gateway ◀── Service Proxy ◀── Backend Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
Request: Client ──▶ Gateway ──▶ Route Match ──▶ Version Select ──▶ Canary Engine │ ▼ Service Proxy (circuit breaker + rate limit) ──▶ Discovery ──▶ Backend Response: Client ◀── Gateway ◀── Service Proxy ◀── Backend CODE_BLOCK:
Request: Client ──▶ Gateway ──▶ Route Match ──▶ Version Select ──▶ Canary Engine │ ▼ Service Proxy (circuit breaker + rate limit) ──▶ Discovery ──▶ Backend Response: Client ◀── Gateway ◀── Service Proxy ◀── Backend CODE_BLOCK:
1. /api/users/:id/orders (most specific)
2. /api/users/:id
3. /api/users/*
4. /api/users
5. /api/**
6. /** (catch-all) Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
1. /api/users/:id/orders (most specific)
2. /api/users/:id
3. /api/users/*
4. /api/users
5. /api/**
6. /** (catch-all) CODE_BLOCK:
1. /api/users/:id/orders (most specific)
2. /api/users/:id
3. /api/users/*
4. /api/users
5. /api/**
6. /** (catch-all) CODE_BLOCK:
{ path: '/api/users/**', serviceName: 'user-service', serviceConfig: { stripPrefix: '/api/users', // Remove from incoming path addPrefix: '/users', // Add to forwarded path },
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
{ path: '/api/users/**', serviceName: 'user-service', serviceConfig: { stripPrefix: '/api/users', // Remove from incoming path addPrefix: '/users', // Add to forwarded path },
} CODE_BLOCK:
{ path: '/api/users/**', serviceName: 'user-service', serviceConfig: { stripPrefix: '/api/users', // Remove from incoming path addPrefix: '/users', // Add to forwarded path },
} COMMAND_BLOCK:
npm install @hazeljs/gateway @hazeljs/discovery @hazeljs/resilience Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
npm install @hazeljs/gateway @hazeljs/discovery @hazeljs/resilience COMMAND_BLOCK:
npm install @hazeljs/gateway @hazeljs/discovery @hazeljs/resilience CODE_BLOCK:
import { GatewayServer } from '@hazeljs/gateway';
import { MemoryRegistryBackend } from '@hazeljs/discovery'; const backend = new MemoryRegistryBackend(); // Register your services with the backend (see discovery docs)
// await registerUserService(backend, 3001); const gateway = GatewayServer.fromConfig( { routes: [ { path: '/api/users/**', serviceName: 'user-service', serviceConfig: { stripPrefix: '/api/users', addPrefix: '/users', }, }, ], }, backend
); // Handle a request
const response = await gateway.handleRequest({ method: 'GET', path: '/api/users', headers: { 'content-type': 'application/json' },
}); Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
import { GatewayServer } from '@hazeljs/gateway';
import { MemoryRegistryBackend } from '@hazeljs/discovery'; const backend = new MemoryRegistryBackend(); // Register your services with the backend (see discovery docs)
// await registerUserService(backend, 3001); const gateway = GatewayServer.fromConfig( { routes: [ { path: '/api/users/**', serviceName: 'user-service', serviceConfig: { stripPrefix: '/api/users', addPrefix: '/users', }, }, ], }, backend
); // Handle a request
const response = await gateway.handleRequest({ method: 'GET', path: '/api/users', headers: { 'content-type': 'application/json' },
}); CODE_BLOCK:
import { GatewayServer } from '@hazeljs/gateway';
import { MemoryRegistryBackend } from '@hazeljs/discovery'; const backend = new MemoryRegistryBackend(); // Register your services with the backend (see discovery docs)
// await registerUserService(backend, 3001); const gateway = GatewayServer.fromConfig( { routes: [ { path: '/api/users/**', serviceName: 'user-service', serviceConfig: { stripPrefix: '/api/users', addPrefix: '/users', }, }, ], }, backend
); // Handle a request
const response = await gateway.handleRequest({ method: 'GET', path: '/api/users', headers: { 'content-type': 'application/json' },
}); COMMAND_BLOCK:
# Start mock backend services
npm run start:services # Config-driven gateway
npm run demo:gateway:config # Gateway with HazelApp (production-style)
npm run demo:gateway:hazelapp # Full-stack integration
npm run demo:scenario:full-stack Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
# Start mock backend services
npm run start:services # Config-driven gateway
npm run demo:gateway:config # Gateway with HazelApp (production-style)
npm run demo:gateway:hazelapp # Full-stack integration
npm run demo:scenario:full-stack COMMAND_BLOCK:
# Start mock backend services
npm run start:services # Config-driven gateway
npm run demo:gateway:config # Gateway with HazelApp (production-style)
npm run demo:gateway:hazelapp # Full-stack integration
npm run demo:scenario:full-stack CODE_BLOCK:
{ path: '/api/users/**', serviceName: 'user-service', versionRoute: { strategy: 'header', header: 'X-API-Version', defaultVersion: 'v1', routes: { v1: { weight: 80, allowExplicit: true, filter: { tags: ['v1'] } }, v2: { weight: 20, allowExplicit: true, filter: { tags: ['v2'] } }, }, },
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
{ path: '/api/users/**', serviceName: 'user-service', versionRoute: { strategy: 'header', header: 'X-API-Version', defaultVersion: 'v1', routes: { v1: { weight: 80, allowExplicit: true, filter: { tags: ['v1'] } }, v2: { weight: 20, allowExplicit: true, filter: { tags: ['v2'] } }, }, },
} CODE_BLOCK:
{ path: '/api/users/**', serviceName: 'user-service', versionRoute: { strategy: 'header', header: 'X-API-Version', defaultVersion: 'v1', routes: { v1: { weight: 80, allowExplicit: true, filter: { tags: ['v1'] } }, v2: { weight: 20, allowExplicit: true, filter: { tags: ['v2'] } }, }, },
} CODE_BLOCK:
Client A (no header) ──────────▶ Gateway ──▶ user-service v1 (80% traffic)
Client B (X-API-Version: v2) ──▶ Gateway ──▶ user-service v2 (20% traffic) Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
Client A (no header) ──────────▶ Gateway ──▶ user-service v1 (80% traffic)
Client B (X-API-Version: v2) ──▶ Gateway ──▶ user-service v2 (20% traffic) CODE_BLOCK:
Client A (no header) ──────────▶ Gateway ──▶ user-service v1 (80% traffic)
Client B (X-API-Version: v2) ──▶ Gateway ──▶ user-service v2 (20% traffic) COMMAND_BLOCK:
Deploy v2 ──▶ 10% canary ──▶ Monitor 5 min │ ┌───────────────┴───────────────┐ │ │ errors < 5% errors > 5% │ │ ▼ ▼ 25% ──▶ 50% ──▶ 75% ──▶ 100% ✓ Rollback 0% ✗ (Complete) (Abort) Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
Deploy v2 ──▶ 10% canary ──▶ Monitor 5 min │ ┌───────────────┴───────────────┐ │ │ errors < 5% errors > 5% │ │ ▼ ▼ 25% ──▶ 50% ──▶ 75% ──▶ 100% ✓ Rollback 0% ✗ (Complete) (Abort) COMMAND_BLOCK:
Deploy v2 ──▶ 10% canary ──▶ Monitor 5 min │ ┌───────────────┴───────────────┐ │ │ errors < 5% errors > 5% │ │ ▼ ▼ 25% ──▶ 50% ──▶ 75% ──▶ 100% ✓ Rollback 0% ✗ (Complete) (Abort) COMMAND_BLOCK:
{ path: '/api/orders/**', serviceName: 'order-service', canary: { stable: { version: '1.0.0', weight: 90, filter: { metadata: { version: '1.0.0' } }, }, canary: { version: '2.0.0', weight: 10, filter: { metadata: { version: '2.0.0' } }, }, promotion: { strategy: 'error-rate', errorThreshold: 5, // Rollback if errors > 5% evaluationWindow: '5m', // Evaluate over 5 minutes autoPromote: true, autoRollback: true, steps: [10, 25, 50, 75, 100], stepInterval: '10m', minRequests: 10, // Wait for enough data }, },
} Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
{ path: '/api/orders/**', serviceName: 'order-service', canary: { stable: { version: '1.0.0', weight: 90, filter: { metadata: { version: '1.0.0' } }, }, canary: { version: '2.0.0', weight: 10, filter: { metadata: { version: '2.0.0' } }, }, promotion: { strategy: 'error-rate', errorThreshold: 5, // Rollback if errors > 5% evaluationWindow: '5m', // Evaluate over 5 minutes autoPromote: true, autoRollback: true, steps: [10, 25, 50, 75, 100], stepInterval: '10m', minRequests: 10, // Wait for enough data }, },
} COMMAND_BLOCK:
{ path: '/api/orders/**', serviceName: 'order-service', canary: { stable: { version: '1.0.0', weight: 90, filter: { metadata: { version: '1.0.0' } }, }, canary: { version: '2.0.0', weight: 10, filter: { metadata: { version: '2.0.0' } }, }, promotion: { strategy: 'error-rate', errorThreshold: 5, // Rollback if errors > 5% evaluationWindow: '5m', // Evaluate over 5 minutes autoPromote: true, autoRollback: true, steps: [10, 25, 50, 75, 100], stepInterval: '10m', minRequests: 10, // Wait for enough data }, },
} COMMAND_BLOCK:
gateway.on('canary:promote', (data) => { console.log(`Step ${data.step}: canary at ${data.canaryWeight}%`);
}); gateway.on('canary:rollback', (data) => { console.log(`Rolled back: ${data.canaryVersion} (trigger: ${data.trigger})`);
}); gateway.on('canary:complete', (data) => { console.log(`${data.version} is now at 100%`);
}); Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
gateway.on('canary:promote', (data) => { console.log(`Step ${data.step}: canary at ${data.canaryWeight}%`);
}); gateway.on('canary:rollback', (data) => { console.log(`Rolled back: ${data.canaryVersion} (trigger: ${data.trigger})`);
}); gateway.on('canary:complete', (data) => { console.log(`${data.version} is now at 100%`);
}); COMMAND_BLOCK:
gateway.on('canary:promote', (data) => { console.log(`Step ${data.step}: canary at ${data.canaryWeight}%`);
}); gateway.on('canary:rollback', (data) => { console.log(`Rolled back: ${data.canaryVersion} (trigger: ${data.trigger})`);
}); gateway.on('canary:complete', (data) => { console.log(`${data.version} is now at 100%`);
}); CODE_BLOCK:
const engine = gateway.getCanaryEngine('/api/orders/**'); engine.pause(); // Freeze canary progression
engine.resume(); // Resume
engine.promote(); // Force next step
engine.rollback(); // Force rollback to 0% Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
const engine = gateway.getCanaryEngine('/api/orders/**'); engine.pause(); // Freeze canary progression
engine.resume(); // Resume
engine.promote(); // Force next step
engine.rollback(); // Force rollback to 0% CODE_BLOCK:
const engine = gateway.getCanaryEngine('/api/orders/**'); engine.pause(); // Freeze canary progression
engine.resume(); // Resume
engine.promote(); // Force next step
engine.rollback(); // Force rollback to 0% CODE_BLOCK:
CLOSED (Normal) │ │ failures >= threshold ▼ OPEN (Blocking) │ │ reset timeout ▼ HALF_OPEN (Testing) │ ┌─────┴─────┐ │ │ success failure │ │ ▼ ▼ CLOSED OPEN Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
CLOSED (Normal) │ │ failures >= threshold ▼ OPEN (Blocking) │ │ reset timeout ▼ HALF_OPEN (Testing) │ ┌─────┴─────┐ │ │ success failure │ │ ▼ ▼ CLOSED OPEN CODE_BLOCK:
CLOSED (Normal) │ │ failures >= threshold ▼ OPEN (Blocking) │ │ reset timeout ▼ HALF_OPEN (Testing) │ ┌─────┴─────┐ │ │ success failure │ │ ▼ ▼ CLOSED OPEN CODE_BLOCK:
{ path: '/api/payments/**', serviceName: 'payment-service', circuitBreaker: { failureThreshold: 3, successThreshold: 2, resetTimeout: 15_000, slidingWindow: { type: 'time', size: 60_000 }, },
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
{ path: '/api/payments/**', serviceName: 'payment-service', circuitBreaker: { failureThreshold: 3, successThreshold: 2, resetTimeout: 15_000, slidingWindow: { type: 'time', size: 60_000 }, },
} CODE_BLOCK:
{ path: '/api/payments/**', serviceName: 'payment-service', circuitBreaker: { failureThreshold: 3, successThreshold: 2, resetTimeout: 15_000, slidingWindow: { type: 'time', size: 60_000 }, },
} CODE_BLOCK:
{ path: '/api/orders/**', rateLimit: { strategy: 'sliding-window', max: 100, window: 60_000, // 100 requests per minute },
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
{ path: '/api/orders/**', rateLimit: { strategy: 'sliding-window', max: 100, window: 60_000, // 100 requests per minute },
} CODE_BLOCK:
{ path: '/api/orders/**', rateLimit: { strategy: 'sliding-window', max: 100, window: 60_000, // 100 requests per minute },
} CODE_BLOCK:
trafficPolicy: { timeout: 8_000, retry: { maxAttempts: 3, backoff: 'exponential', baseDelay: 1_000, maxDelay: 10_000, jitter: true, },
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
trafficPolicy: { timeout: 8_000, retry: { maxAttempts: 3, backoff: 'exponential', baseDelay: 1_000, maxDelay: 10_000, jitter: true, },
} CODE_BLOCK:
trafficPolicy: { timeout: 8_000, retry: { maxAttempts: 3, backoff: 'exponential', baseDelay: 1_000, maxDelay: 10_000, jitter: true, },
} CODE_BLOCK:
{ path: '/api/products/**', serviceName: 'product-service', trafficPolicy: { mirror: { service: 'analytics-service', percentage: 30, waitForResponse: false, // Fire-and-forget }, timeout: 5_000, },
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
{ path: '/api/products/**', serviceName: 'product-service', trafficPolicy: { mirror: { service: 'analytics-service', percentage: 30, waitForResponse: false, // Fire-and-forget }, timeout: 5_000, },
} CODE_BLOCK:
{ path: '/api/products/**', serviceName: 'product-service', trafficPolicy: { mirror: { service: 'analytics-service', percentage: 30, waitForResponse: false, // Fire-and-forget }, timeout: 5_000, },
} CODE_BLOCK:
import { HazelApp, HazelModule } from '@hazeljs/core';
import { GatewayServer, createGatewayHandler } from '@hazeljs/gateway'; const gateway = GatewayServer.fromConfig(config, backend);
gateway.startCanaries(); const app = new HazelApp(AppModule);
app.addProxyHandler('/api', createGatewayHandler(gateway));
await app.listen(3000); Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
import { HazelApp, HazelModule } from '@hazeljs/core';
import { GatewayServer, createGatewayHandler } from '@hazeljs/gateway'; const gateway = GatewayServer.fromConfig(config, backend);
gateway.startCanaries(); const app = new HazelApp(AppModule);
app.addProxyHandler('/api', createGatewayHandler(gateway));
await app.listen(3000); CODE_BLOCK:
import { HazelApp, HazelModule } from '@hazeljs/core';
import { GatewayServer, createGatewayHandler } from '@hazeljs/gateway'; const gateway = GatewayServer.fromConfig(config, backend);
gateway.startCanaries(); const app = new HazelApp(AppModule);
app.addProxyHandler('/api', createGatewayHandler(gateway));
await app.listen(3000); COMMAND_BLOCK:
const gatewayConfig = () => ({ gateway: { routes: [ { path: '/api/orders/**', serviceName: 'order-service', canary: { stable: { version: process.env.ORDER_STABLE_VERSION || 'v1', weight: parseInt(process.env.ORDER_STABLE_WEIGHT || '90'), }, canary: { version: process.env.ORDER_CANARY_VERSION || 'v2', weight: parseInt(process.env.ORDER_CANARY_WEIGHT || '10'), }, promotion: { errorThreshold: parseInt(process.env.ORDER_CANARY_ERROR_THRESHOLD || '5'), evaluationWindow: process.env.ORDER_CANARY_WINDOW || '5m', }, }, }, ], },
}); Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
const gatewayConfig = () => ({ gateway: { routes: [ { path: '/api/orders/**', serviceName: 'order-service', canary: { stable: { version: process.env.ORDER_STABLE_VERSION || 'v1', weight: parseInt(process.env.ORDER_STABLE_WEIGHT || '90'), }, canary: { version: process.env.ORDER_CANARY_VERSION || 'v2', weight: parseInt(process.env.ORDER_CANARY_WEIGHT || '10'), }, promotion: { errorThreshold: parseInt(process.env.ORDER_CANARY_ERROR_THRESHOLD || '5'), evaluationWindow: process.env.ORDER_CANARY_WINDOW || '5m', }, }, }, ], },
}); COMMAND_BLOCK:
const gatewayConfig = () => ({ gateway: { routes: [ { path: '/api/orders/**', serviceName: 'order-service', canary: { stable: { version: process.env.ORDER_STABLE_VERSION || 'v1', weight: parseInt(process.env.ORDER_STABLE_WEIGHT || '90'), }, canary: { version: process.env.ORDER_CANARY_VERSION || 'v2', weight: parseInt(process.env.ORDER_CANARY_WEIGHT || '10'), }, promotion: { errorThreshold: parseInt(process.env.ORDER_CANARY_ERROR_THRESHOLD || '5'), evaluationWindow: process.env.ORDER_CANARY_WINDOW || '5m', }, }, }, ], },
}); - Introduction
- Why an API Gateway?
- Architecture Overview
- Core Concepts
- Getting Started
- Version Routing
- Canary Deployments
- Resilience Patterns
- Traffic Management
- Production Integration
- Best Practices - Unified entry point — Clients talk to one URL; the gateway routes to the right service
- Version routing — Route by header, URI, query param, or weighted distribution
- Canary deployments — Gradually shift traffic with automatic promotion or rollback
- Resilience — Circuit breaker, rate limiting, retries, and timeouts per route
- Traffic mirroring — Shadow traffic to test new versions without affecting users
- Real-time metrics — Per-route and per-version performance tracking - Memory backend — Development (in-memory)
- Redis backend — Production (shared state)
- Consul backend — Service mesh
- Kubernetes backend — K8s Endpoints API - Health checks (/health, /ready, /live)
- CORS, body parsing, request ID
- Graceful shutdown
- Single process for gateway + custom controllers - Too short: Not enough data, noisy decisions
- Too long: Users exposed to bugs longer
- 5 minutes is a good default - Gateway Package Documentation
- Discovery Package
- Resilience Package
- hazeljs-gateway-starter Demos — Run the examples in this repo
how-totutorialguidedev.toaimlserverroutingroutersslkubernetesk8s