Skip to content
Plurafy

API Gateway Pattern

API Gateway acts as a single entry point for microservices. Learn to implement API Gateways for request routing, authentication, and rate limiting.

Implement an API Gateway that provides unified access to microservices with authentication, rate limiting, and request routing.

  • Routing: Direct requests to services
  • Authentication: Verify requests
  • Rate Limiting: Control traffic
  • Transformation: Modify requests/responses
  • Aggregation: Combine service responses
  • Load Balancing: Distribute load
  • Single Entry Point: Simplified client interaction
  • Cross-Cutting Concerns: Centralized logic
  • Service Abstraction: Hide service complexity
  • Protocol Translation: Different protocols
  • API Versioning: Multiple API versions
const express = require('express');
const httpProxy = require('express-http-proxy');
const rateLimit = require('express-rate-limit');
const jwt = require('jsonwebtoken');
const app = express();
// Service URLs
const services = {
users: process.env.USER_SERVICE_URL || 'http://user-service:3001',
orders: process.env.ORDER_SERVICE_URL || 'http://order-service:3002',
products: process.env.PRODUCT_SERVICE_URL || 'http://product-service:3003',
notifications: process.env.NOTIFICATION_SERVICE_URL || 'http://notification-service:3004',
};
// Authentication middleware
const authMiddleware = (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
res.status(401).json({ error: 'Invalid token' });
}
};
// Rate limiter
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
});
app.use(limiter);
app.use(express.json());
// Health check
app.get('/health', (req, res) => {
res.json({ status: 'Gateway is running' });
});
// Routes to services
app.use('/api/users', httpProxy(services.users, {
proxyReqPathResolver: (req) => `/api/users${req.url}`,
}));
app.use('/api/orders', authMiddleware, httpProxy(services.orders, {
proxyReqPathResolver: (req) => `/api/orders${req.url}`,
}));
app.use('/api/products', httpProxy(services.products, {
proxyReqPathResolver: (req) => `/api/products${req.url}`,
}));
// Request logging
app.use((req, res, next) => {
console.log(`${req.method} ${req.path} from ${req.ip}`);
next();
});
app.listen(8000, () => {
console.log('API Gateway running on port 8000');
});

2. Advanced Gateway with Service Aggregation

Section titled “2. Advanced Gateway with Service Aggregation”
class APIGateway {
constructor() {
this.services = new Map();
this.middleware = [];
this.routes = [];
}
registerService(name, url) {
this.services.set(name, url);
}
use(middleware) {
this.middleware.push(middleware);
}
route(path, method, service, options = {}) {
this.routes.push({
path,
method: method.toUpperCase(),
service,
...options,
});
}
async handleRequest(req, res) {
// Find matching route
const route = this.routes.find(r =>
r.path === req.path && r.method === req.method.toUpperCase()
);
if (!route) {
return res.status(404).json({ error: 'Not found' });
}
// Apply middleware
for (const mw of this.middleware) {
await new Promise((resolve, reject) => {
mw(req, res, (err) => {
if (err) reject(err);
else resolve();
});
});
}
// Forward request
const serviceUrl = this.services.get(route.service);
const response = await this.forwardRequest(serviceUrl, req, route);
res.status(response.status).json(response.data);
}
async forwardRequest(serviceUrl, req, route) {
try {
const response = await fetch(`${serviceUrl}${req.path}`, {
method: req.method,
headers: { ...req.headers, 'X-Gateway': 'true' },
body: req.body ? JSON.stringify(req.body) : null,
});
const data = await response.json();
return { status: response.status, data };
} catch (error) {
return {
status: 503,
data: { error: 'Service unavailable' },
};
}
}
// Aggregate multiple service responses
async aggregateServices(req, serviceList) {
const results = {};
for (const service of serviceList) {
try {
const response = await fetch(`${this.services.get(service)}/data`);
results[service] = await response.json();
} catch (error) {
results[service] = { error: error.message };
}
}
return results;
}
}
// Usage
const gateway = new APIGateway();
gateway.registerService('users', 'http://user-service:3001');
gateway.registerService('orders', 'http://order-service:3002');
gateway.use((req, res, next) => {
console.log(`${req.method} ${req.path}`);
next();
});
gateway.route('/api/users', 'GET', 'users');
gateway.route('/api/orders', 'GET', 'orders');
gateway.route('/api/dashboard', 'GET', null, {
async handler(req, res) {
const data = await gateway.aggregateServices(req, ['users', 'orders']);
res.json(data);
},
});
kong-config.yml
_format_version: "1.1"
_transform: true
services:
- name: user-service
url: http://user-service:3001
routes:
- name: user-get
paths:
- /api/users
methods:
- GET
- name: user-create
paths:
- /api/users
methods:
- POST
plugins:
- name: rate-limiting
config:
minute: 100
policy: local
- name: jwt
config:
key_claim_name: sub
- name: cors
config:
origins:
- http://localhost:3000
methods:
- GET
- POST
- PUT
- DELETE
- name: order-service
url: http://order-service:3002
routes:
- name: order-get
paths:
- /api/orders
methods:
- GET
plugins:
- name: rate-limiting
config:
minute: 50
- name: jwt
class GatewayMiddlewarePipeline {
constructor() {
this.middlewares = [];
}
use(middleware) {
this.middlewares.push(middleware);
}
async execute(req, res, context = {}) {
let index = 0;
const next = async () => {
if (index < this.middlewares.length) {
const middleware = this.middlewares[index++];
await middleware(req, res, context, next);
}
};
await next();
}
}
// Middleware implementations
const authMiddleware = async (req, res, context, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'No token' });
}
context.user = jwt.verify(token, process.env.JWT_SECRET);
await next();
};
const rateLimitMiddleware = async (req, res, context, next) => {
const clientId = context.user?.id || req.ip;
const limit = await checkRateLimit(clientId);
if (!limit) {
return res.status(429).json({ error: 'Rate limit exceeded' });
}
await next();
};
const loggingMiddleware = async (req, res, context, next) => {
const start = Date.now();
await next();
const duration = Date.now() - start;
console.log(`${req.method} ${req.path} ${duration}ms`);
};
// Setup pipeline
const pipeline = new GatewayMiddlewarePipeline();
pipeline.use(authMiddleware);
pipeline.use(rateLimitMiddleware);
pipeline.use(loggingMiddleware);
const Consul = require('consul');
class DynamicGateway {
constructor() {
this.consul = new Consul({
host: 'consul-server',
port: 8500,
});
this.services = new Map();
}
async discoverServices() {
try {
const services = await this.consul.catalog.service.list();
for (const [name, meta] of Object.entries(services)) {
const instances = await this.consul.catalog.service.nodes(name);
this.services.set(name, instances.map(i => `http://${i.ServiceAddress}:${i.ServicePort}`));
}
} catch (error) {
console.error('Service discovery error:', error);
}
}
selectInstance(serviceName) {
const instances = this.services.get(serviceName);
if (!instances || instances.length === 0) {
throw new Error(`No instances for service ${serviceName}`);
}
// Round-robin selection
const index = Math.floor(Math.random() * instances.length);
return instances[index];
}
async handleRequest(req, res, serviceName) {
const instanceUrl = this.selectInstance(serviceName);
try {
const response = await fetch(`${instanceUrl}${req.path}`, {
method: req.method,
headers: req.headers,
body: req.body ? JSON.stringify(req.body) : null,
});
res.status(response.status);
res.json(await response.json());
} catch (error) {
res.status(503).json({ error: 'Service unavailable' });
}
}
}
// Start discovery
const gateway = new DynamicGateway();
setInterval(() => gateway.discoverServices(), 30000);
  1. Routing

    • Clear path patterns
    • Version handling
    • Service abstraction
    • Load balancing
  2. Security

    • Authenticate all requests
    • Validate inputs
    • Rate limiting
    • HTTPS enforcement
  3. Performance

    • Connection pooling
    • Caching
    • Compression
    • Monitoring
  4. Reliability

    • Service discovery
    • Failover handling
    • Circuit breakers
    • Timeouts
  5. Observability

    • Request logging
    • Tracing
    • Metrics collection
    • Error tracking
  • Microservices: Unified entry point
  • Multiple Versions: API versioning
  • Mobile Backends: BFF architecture
  • Partner APIs: Controlled access
  • Legacy Integration: Modernizing APIs