Skip to main content
Course/Module 10/Topic 3 of 4Advanced

API Gateway Implementation

Learn API Gateway patterns for microservices including routing, load balancing, authentication, and rate limiting.

90 minBy Priygop TeamLast 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');
});

Additional Resources

Recommended Reading

  • Microservices Architecture Guide
  • API Gateway Patterns
  • Service Communication Patterns
  • Container orchestration Best Practices

Online Resources

  • Microservices Tutorial
  • Docker Compose Guide
  • Kubernetes Documentation
  • Service Mesh Patterns
Chat on WhatsApp
Priygop - Leading Professional Development Platform | Expert Courses & Interview Prep