API Gateway Implementation
Learn API Gateway patterns for microservices including routing, load balancing, authentication, and rate limiting.
90 min•By Priygop Team•Last updated: Feb 2026
API Gateway Fundamentals
An API Gateway is a single entry point for all client requests to microservices, providing routing, load balancing, authentication, and other cross-cutting concerns.
Basic API Gateway
Example
// Express-based API Gateway
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const rateLimit = require('express-rate-limit');
const helmet = require('helmet');
const app = express();
// security middleware
app.use(helmet());
// Rate limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // limit each IP to 100 requests per windowMs
});
app.use(limiter);
// Service registry
const services = {
user: {
url: process.env.USER_SERVICE_URL || 'http://localhost:3001',
health: '/health'
},
product: {
url: process.env.PRODUCT_SERVICE_URL || 'http://localhost:3002',
health: '/health'
},
order: {
url: process.env.ORDER_SERVICE_URL || 'http://localhost:3003',
health: '/health'
}
};
// Health check for all services
app.get('/health', async (req, res) => {
const healthStatus = {};
for (const [serviceName, service] of Object.entries(services)) {
try {
const response = await fetch(`${service.url}${service.health}`);
healthStatus[serviceName] = {
status: response.ok ? 'healthy' : 'unhealthy',
responseTime: Date.now() - startTime
};
} catch (error) {
healthStatus[serviceName] = {
status: 'unhealthy',
error: error.message
};
}
}
res.json(healthStatus);
});Authentication & Authorization
Example
// JWT-based authentication middleware
const jwt = require('jsonwebtoken');
const authenticateToken = (req, res, next) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Access token required' });
}
jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) {
return res.status(403).json({ error: 'Invalid token' });
}
req.user = user;
next();
});
};
// Role-based authorization
const authorize = (roles) => {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({ error: 'Authentication required' });
}
if (!roles.includes(req.user.role)) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
};
// Usage
app.use('/api/users', authenticateToken);
app.use('/api/admin', authenticateToken, authorize(['admin']));Request Routing & Load Balancing
Example
// Advanced routing with load balancing
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
class LoadBalancer {
constructor(services) {
this.services = services;
this.currentIndex = 0;
}
getNextService() {
const service = this.services[this.currentIndex];
this.currentIndex = (this.currentIndex + 1) % this.services.length;
return service;
}
getHealthyServices() {
return this.services.filter(service => service.healthy);
}
}
// Service discovery and health checking
class ServiceRegistry {
constructor() {
this.services = new Map();
}
register(serviceName, serviceUrl) {
this.services.set(serviceName, {
url: serviceUrl,
healthy: true,
lastCheck: Date.now()
});
}
async checkHealth(serviceName) {
const service = this.services.get(serviceName);
if (!service) return false;
try {
const response = await fetch(`${service.url}/health`);
service.healthy = response.ok;
service.lastCheck = Date.now();
return service.healthy;
} catch (error) {
service.healthy = false;
service.lastCheck = Date.now();
return false;
}
}
getHealthyServices(serviceName) {
const service = this.services.get(serviceName);
return service && service.healthy ? [service] : [];
}
}
// API Gateway with load balancing
const registry = new ServiceRegistry();
registry.register('user', 'http://user-service-1:3001');
registry.register('user', 'http://user-service-2:3001');
app.use('/api/users', (req, res, next) => {
const healthyServices = registry.getHealthyServices('user');
if (healthyServices.length === 0) {
return res.status(503).json({ error: 'Service unavailable' });
}
const loadBalancer = new LoadBalancer(healthyServices);
const targetService = loadBalancer.getNextService();
const proxy = createProxyMiddleware({
target: targetService.url,
changeOrigin: true,
onError: (err, req, res) => {
res.status(503).json({ error: 'Service unavailable' });
}
});
proxy(req, res, next);
});Rate Limiting & Throttling
Example
// Advanced rate limiting
const rateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis');
const Redis = require('ioredis');
const redis = new Redis(process.env.REDIS_URL);
// Different rate limits for different endpoints
const createRateLimit = (windowMs, max, message) => {
return rateLimit({
store: new RedisStore({
sendCommand: (...args) => redis.call(...args),
}),
windowMs,
max,
message: { error: message },
standardHeaders: true,
legacyHeaders: false,
});
};
// Global rate limit
app.use(createRateLimit(15 * 60 * 1000, 100, 'Too many requests from this IP'));
// User-specific rate limit
app.use('/api/users', createRateLimit(15 * 60 * 1000, 50, 'Too many user requests'));
// Admin rate limit
app.use('/api/admin', createRateLimit(15 * 60 * 1000, 200, 'Too many admin requests'));Mini-Project: Complete API Gateway
Example
// Complete API Gateway implementation
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const rateLimit = require('express-rate-limit');
const helmet = require('helmet');
const jwt = require('jsonwebtoken');
const Redis = require('ioredis');
const app = express();
const redis = new Redis(process.env.REDIS_URL);
// Middleware
app.use(helmet());
app.use(express.json());
// Rate limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
standardHeaders: true,
legacyHeaders: false,
});
app.use(limiter);
// Service registry
class ServiceRegistry {
constructor() {
this.services = new Map();
}
register(name, url, healthEndpoint = '/health') {
if (!this.services.has(name)) {
this.services.set(name, []);
}
this.services.get(name).push({ url, healthEndpoint, healthy: true });
}
async checkHealth() {
for (const [serviceName, instances] of this.services) {
for (const instance of instances) {
try {
const response = await fetch(`${instance.url}${instance.healthEndpoint}`);
instance.healthy = response.ok;
} catch (error) {
instance.healthy = false;
}
}
}
}
getHealthyInstances(serviceName) {
const instances = this.services.get(serviceName) || [];
return instances.filter(instance => instance.healthy);
}
}
const registry = new ServiceRegistry();
// Register services
registry.register('user', 'http://user-service:3001');
registry.register('product', 'http://product-service:3002');
registry.register('order', 'http://order-service:3003');
// Health check every 30 seconds
setInterval(() => {
registry.checkHealth();
}, 30000);
// Authentication middleware
const authenticateToken = (req, res, next) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Access token required' });
}
jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) {
return res.status(403).json({ error: 'Invalid token' });
}
req.user = user;
next();
});
};
// Load balancer
class LoadBalancer {
constructor(instances) {
this.instances = instances;
this.currentIndex = 0;
}
getNextInstance() {
if (this.instances.length === 0) {
throw new Error('No healthy instances available');
}
const instance = this.instances[this.currentIndex];
this.currentIndex = (this.currentIndex + 1) % this.instances.length;
return instance;
}
}
// Proxy middleware factory
const createProxy = (serviceName) => {
return (req, res, next) => {
const healthyInstances = registry.getHealthyInstances(serviceName);
if (healthyInstances.length === 0) {
return res.status(503).json({
error: 'Service unavailable',
service: serviceName
});
}
const loadBalancer = new LoadBalancer(healthyInstances);
const targetInstance = loadBalancer.getNextInstance();
const proxy = createProxyMiddleware({
target: targetInstance.url,
changeOrigin: true,
onError: (err, req, res) => {
res.status(503).json({
error: 'Service unavailable',
service: serviceName
});
}
});
proxy(req, res, next);
};
};
// Routes
app.get('/health', (req, res) => {
const healthStatus = {};
for (const [serviceName, instances] of registry.services) {
healthStatus[serviceName] = instances.map(instance => ({
url: instance.url,
healthy: instance.healthy
}));
}
res.json(healthStatus);
});
// Service routes
app.use('/api/users', authenticateToken, createProxy('user'));
app.use('/api/products', createProxy('product'));
app.use('/api/orders', authenticateToken, createProxy('order'));
// Error handling
app.use((err, req, res, next) => {
console.error('Gateway error:', err);
res.status(500).json({ error: 'Internal gateway error' });
});
app.listen(3000, () => {
console.log('API Gateway running on port 3000');
});