Skip to main content

API Gateway Pattern

An API Gateway is the single entry point for all client requests to your microservices. It handles cross-cutting concerns and provides a unified interface.
Learning Objectives:
  • Understand API Gateway responsibilities
  • Implement routing and load balancing
  • Add authentication and authorization
  • Implement rate limiting and throttling
  • Build request aggregation patterns

Why API Gateway?

Without API Gateway

                CLIENT

    ┌─────────────┼─────────────┐
    │             │             │
    ▼             ▼             ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ User    │ │ Order   │ │ Product │
│ Service │ │ Service │ │ Service │
└─────────┘ └─────────┘ └─────────┘

Problems:
• Client needs to know all service URLs
• Each service implements auth, rate limiting
• No unified error handling
• CORS issues
• Multiple round trips
• Exposing internal services

With API Gateway

                CLIENT


          ┌─────────────┐
          │ API GATEWAY │
          │ ─────────── │
          │ • Routing   │
          │ • Auth      │
          │ • Rate Limit│
          │ • Caching   │
          │ • Logging   │
          └──────┬──────┘
    ┌─────────────┼─────────────┐
    │             │             │
    ▼             ▼             ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ User    │ │ Order   │ │ Product │
│ Service │ │ Service │ │ Service │
└─────────┘ └─────────┘ └─────────┘

Benefits:
• Single entry point
• Centralized cross-cutting concerns
• Protocol translation
• Request aggregation
• Service discovery abstraction

API Gateway Responsibilities

┌─────────────────────────────────────────────────────────────────────────────┐
│                         API GATEWAY RESPONSIBILITIES                         │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  ┌─────────────────────┐  ┌─────────────────────┐  ┌─────────────────────┐  │
│  │      ROUTING        │  │   AUTHENTICATION    │  │   RATE LIMITING     │  │
│  │ ─────────────────── │  │ ─────────────────── │  │ ─────────────────── │  │
│  │ • Path-based        │  │ • JWT validation    │  │ • Per-user limits   │  │
│  │ • Method-based      │  │ • API key check     │  │ • Per-IP limits     │  │
│  │ • Header-based      │  │ • OAuth 2.0         │  │ • Global limits     │  │
│  │ • Load balancing    │  │ • Session mgmt      │  │ • Burst handling    │  │
│  └─────────────────────┘  └─────────────────────┘  └─────────────────────┘  │
│                                                                              │
│  ┌─────────────────────┐  ┌─────────────────────┐  ┌─────────────────────┐  │
│  │  REQUEST TRANSFORM  │  │      CACHING        │  │    AGGREGATION      │  │
│  │ ─────────────────── │  │ ─────────────────── │  │ ─────────────────── │  │
│  │ • Header injection  │  │ • Response caching  │  │ • Combine responses │  │
│  │ • Body transform    │  │ • Cache invalidation│  │ • Reduce round trips│  │
│  │ • Protocol convert  │  │ • Cache keys        │  │ • BFF pattern       │  │
│  │ • Versioning        │  │ • TTL management    │  │ • GraphQL facade    │  │
│  └─────────────────────┘  └─────────────────────┘  └─────────────────────┘  │
│                                                                              │
│  ┌─────────────────────┐  ┌─────────────────────┐  ┌─────────────────────┐  │
│  │     MONITORING      │  │  CIRCUIT BREAKER    │  │      SECURITY       │  │
│  │ ─────────────────── │  │ ─────────────────── │  │ ─────────────────── │  │
│  │ • Request logging   │  │ • Failure detection │  │ • CORS handling     │  │
│  │ • Metrics           │  │ • Fallback routes   │  │ • WAF integration   │  │
│  │ • Tracing           │  │ • Recovery          │  │ • IP filtering      │  │
│  │ • Alerting          │  │ • Health checks     │  │ • TLS termination   │  │
│  └─────────────────────┘  └─────────────────────┘  └─────────────────────┘  │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Building an API Gateway with Express

Basic Gateway Structure

// gateway/src/app.js
const express = require('express');
const helmet = require('helmet');
const cors = require('cors');
const morgan = require('morgan');
const { createProxyMiddleware } = require('http-proxy-middleware');

const app = express();

// Security middleware
app.use(helmet());
app.use(cors({
  origin: process.env.ALLOWED_ORIGINS?.split(',') || '*',
  credentials: true
}));

// Request parsing
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true }));

// Logging
app.use(morgan(':method :url :status :response-time ms - :res[content-length]'));

// Request ID middleware
app.use((req, res, next) => {
  req.id = req.headers['x-request-id'] || generateRequestId();
  res.setHeader('X-Request-ID', req.id);
  next();
});

// Health check
app.get('/health', (req, res) => {
  res.json({ status: 'healthy', timestamp: new Date().toISOString() });
});

// Import routes
const authMiddleware = require('./middleware/auth');
const rateLimiter = require('./middleware/rateLimiter');
const routes = require('./routes');

// Apply middleware
app.use(rateLimiter);
app.use('/api', authMiddleware);
app.use('/api', routes);

// Error handling
app.use((err, req, res, next) => {
  console.error(`Error [${req.id}]:`, err);
  
  const statusCode = err.statusCode || 500;
  const message = err.expose ? err.message : 'Internal server error';
  
  res.status(statusCode).json({
    error: {
      code: err.code || 'INTERNAL_ERROR',
      message,
      requestId: req.id
    }
  });
});

module.exports = app;

Service Routing

// gateway/src/routes/index.js
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const router = express.Router();

// Service URLs from environment
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',
  payments: process.env.PAYMENT_SERVICE_URL || 'http://payment-service:3004',
  notifications: process.env.NOTIFICATION_SERVICE_URL || 'http://notification-service:3005'
};

// Proxy configuration factory
const createProxy = (target, pathRewrite = {}) => {
  return createProxyMiddleware({
    target,
    changeOrigin: true,
    pathRewrite,
    timeout: 30000,
    proxyTimeout: 30000,
    onProxyReq: (proxyReq, req) => {
      // Forward authentication info
      if (req.user) {
        proxyReq.setHeader('X-User-ID', req.user.id);
        proxyReq.setHeader('X-User-Email', req.user.email);
        proxyReq.setHeader('X-User-Roles', JSON.stringify(req.user.roles));
      }
      // Forward correlation headers
      proxyReq.setHeader('X-Request-ID', req.id);
      proxyReq.setHeader('X-Correlation-ID', req.correlationId || req.id);
    },
    onProxyRes: (proxyRes, req, res) => {
      // Add security headers
      proxyRes.headers['X-Content-Type-Options'] = 'nosniff';
      proxyRes.headers['X-Frame-Options'] = 'DENY';
    },
    onError: (err, req, res) => {
      console.error(`Proxy error [${req.id}]:`, err.message);
      
      if (!res.headersSent) {
        res.status(503).json({
          error: {
            code: 'SERVICE_UNAVAILABLE',
            message: 'Service temporarily unavailable',
            requestId: req.id
          }
        });
      }
    }
  });
};

// Route to services
router.use('/users', createProxy(services.users, { '^/api/users': '' }));
router.use('/orders', createProxy(services.orders, { '^/api/orders': '' }));
router.use('/products', createProxy(services.products, { '^/api/products': '' }));
router.use('/payments', createProxy(services.payments, { '^/api/payments': '' }));
router.use('/notifications', createProxy(services.notifications, { '^/api/notifications': '' }));

module.exports = router;

Authentication Middleware

// gateway/src/middleware/auth.js
const jwt = require('jsonwebtoken');

const JWT_SECRET = process.env.JWT_SECRET;
const publicPaths = [
  { method: 'POST', path: '/api/users/register' },
  { method: 'POST', path: '/api/users/login' },
  { method: 'GET', path: '/api/products' },
  { method: 'GET', path: /^\/api\/products\/[\w-]+$/ }
];

const isPublicPath = (method, path) => {
  return publicPaths.some(route => {
    const methodMatch = route.method === method || route.method === '*';
    const pathMatch = route.path instanceof RegExp 
      ? route.path.test(path)
      : route.path === path;
    return methodMatch && pathMatch;
  });
};

const authMiddleware = async (req, res, next) => {
  // Skip auth for public paths
  if (isPublicPath(req.method, req.path)) {
    return next();
  }

  const authHeader = req.headers.authorization;
  
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({
      error: {
        code: 'UNAUTHORIZED',
        message: 'Missing or invalid authorization header'
      }
    });
  }

  const token = authHeader.substring(7);
  
  try {
    const decoded = jwt.verify(token, JWT_SECRET);
    
    req.user = {
      id: decoded.sub,
      email: decoded.email,
      roles: decoded.roles || [],
      permissions: decoded.permissions || []
    };
    
    next();
  } catch (error) {
    if (error.name === 'TokenExpiredError') {
      return res.status(401).json({
        error: {
          code: 'TOKEN_EXPIRED',
          message: 'Authentication token has expired'
        }
      });
    }
    
    return res.status(401).json({
      error: {
        code: 'INVALID_TOKEN',
        message: 'Invalid authentication token'
      }
    });
  }
};

// Role-based access control
const requireRoles = (...roles) => {
  return (req, res, next) => {
    if (!req.user) {
      return res.status(401).json({
        error: { code: 'UNAUTHORIZED', message: 'Authentication required' }
      });
    }
    
    const hasRole = roles.some(role => req.user.roles.includes(role));
    
    if (!hasRole) {
      return res.status(403).json({
        error: { code: 'FORBIDDEN', message: 'Insufficient permissions' }
      });
    }
    
    next();
  };
};

module.exports = authMiddleware;
module.exports.requireRoles = requireRoles;

Rate Limiting

// gateway/src/middleware/rateLimiter.js
const rateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis');
const Redis = require('ioredis');

const redis = new Redis(process.env.REDIS_URL);

// Global rate limiter
const globalLimiter = rateLimit({
  store: new RedisStore({
    sendCommand: (...args) => redis.call(...args)
  }),
  windowMs: 60 * 1000, // 1 minute
  max: 1000, // 1000 requests per minute globally
  message: {
    error: {
      code: 'RATE_LIMIT_EXCEEDED',
      message: 'Too many requests, please try again later'
    }
  },
  standardHeaders: true,
  legacyHeaders: false
});

// Per-user rate limiter
const userLimiter = rateLimit({
  store: new RedisStore({
    sendCommand: (...args) => redis.call(...args)
  }),
  windowMs: 60 * 1000,
  max: 100, // 100 requests per minute per user
  keyGenerator: (req) => {
    return req.user?.id || req.ip;
  },
  skip: (req) => {
    // Skip rate limiting for internal services
    return req.headers['x-internal-service'] === process.env.INTERNAL_SECRET;
  }
});

// Endpoint-specific limiters
const createEndpointLimiter = (options) => {
  return rateLimit({
    store: new RedisStore({
      sendCommand: (...args) => redis.call(...args)
    }),
    windowMs: options.windowMs || 60 * 1000,
    max: options.max || 10,
    keyGenerator: (req) => `${req.user?.id || req.ip}:${req.path}`,
    message: {
      error: {
        code: 'ENDPOINT_RATE_LIMIT',
        message: options.message || 'Rate limit exceeded for this endpoint'
      }
    }
  });
};

// Specific endpoint limiters
const authLimiter = createEndpointLimiter({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 5, // 5 login attempts
  message: 'Too many login attempts, please try again later'
});

const passwordResetLimiter = createEndpointLimiter({
  windowMs: 60 * 60 * 1000, // 1 hour
  max: 3,
  message: 'Too many password reset requests'
});

module.exports = {
  globalLimiter,
  userLimiter,
  authLimiter,
  passwordResetLimiter,
  createEndpointLimiter
};

Request Aggregation

// gateway/src/aggregation/orderDetails.js
const axios = require('axios');

class OrderAggregator {
  constructor(services) {
    this.services = services;
  }

  // Aggregate order details from multiple services
  async getOrderDetails(orderId, userId) {
    try {
      // Parallel requests to multiple services
      const [order, user, products] = await Promise.all([
        this.getOrder(orderId),
        this.getUser(userId),
        this.getOrderProducts(orderId)
      ]);

      // Aggregate response
      return {
        order: {
          id: order.id,
          status: order.status,
          total: order.total,
          createdAt: order.createdAt
        },
        customer: {
          id: user.id,
          name: user.name,
          email: user.email
        },
        items: products.map(p => ({
          productId: p.id,
          name: p.name,
          price: p.price,
          quantity: order.items.find(i => i.productId === p.id)?.quantity
        })),
        shipping: order.shippingAddress,
        timeline: await this.getOrderTimeline(orderId)
      };
    } catch (error) {
      throw new AggregationError('Failed to aggregate order details', error);
    }
  }

  async getOrder(orderId) {
    const response = await axios.get(`${this.services.orders}/orders/${orderId}`);
    return response.data;
  }

  async getUser(userId) {
    const response = await axios.get(`${this.services.users}/users/${userId}`);
    return response.data;
  }

  async getOrderProducts(orderId) {
    const order = await this.getOrder(orderId);
    const productIds = order.items.map(i => i.productId);
    
    const response = await axios.post(
      `${this.services.products}/products/batch`,
      { ids: productIds }
    );
    return response.data;
  }

  async getOrderTimeline(orderId) {
    // Get events from multiple sources
    const [orderEvents, shippingEvents] = await Promise.allSettled([
      axios.get(`${this.services.orders}/orders/${orderId}/events`),
      axios.get(`${this.services.shipping}/shipments/order/${orderId}/events`)
    ]);

    const events = [];
    
    if (orderEvents.status === 'fulfilled') {
      events.push(...orderEvents.value.data);
    }
    
    if (shippingEvents.status === 'fulfilled') {
      events.push(...shippingEvents.value.data);
    }

    return events.sort((a, b) => new Date(a.timestamp) - new Date(b.timestamp));
  }

  // Dashboard aggregation
  async getDashboard(userId) {
    const [orders, cart, recommendations, notifications] = await Promise.allSettled([
      axios.get(`${this.services.orders}/users/${userId}/orders?limit=5`),
      axios.get(`${this.services.orders}/users/${userId}/cart`),
      axios.get(`${this.services.products}/users/${userId}/recommendations`),
      axios.get(`${this.services.notifications}/users/${userId}/unread`)
    ]);

    return {
      recentOrders: orders.status === 'fulfilled' ? orders.value.data : [],
      cart: cart.status === 'fulfilled' ? cart.value.data : null,
      recommendations: recommendations.status === 'fulfilled' ? recommendations.value.data : [],
      unreadNotifications: notifications.status === 'fulfilled' ? notifications.value.data.length : 0
    };
  }
}

// Aggregation route
router.get('/aggregate/orders/:orderId', async (req, res, next) => {
  try {
    const aggregator = new OrderAggregator(services);
    const details = await aggregator.getOrderDetails(
      req.params.orderId,
      req.user.id
    );
    res.json(details);
  } catch (error) {
    next(error);
  }
});

router.get('/aggregate/dashboard', async (req, res, next) => {
  try {
    const aggregator = new OrderAggregator(services);
    const dashboard = await aggregator.getDashboard(req.user.id);
    res.json(dashboard);
  } catch (error) {
    next(error);
  }
});

Backend for Frontend (BFF) Pattern

Different clients need different APIs.
┌─────────────────────────────────────────────────────────────────────────────┐
│                         BFF PATTERN                                          │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│   ┌──────────┐    ┌──────────┐    ┌──────────┐                             │
│   │  Mobile  │    │   Web    │    │  Admin   │                             │
│   │   App    │    │   App    │    │  Portal  │                             │
│   └────┬─────┘    └────┬─────┘    └────┬─────┘                             │
│        │               │               │                                    │
│        ▼               ▼               ▼                                    │
│   ┌──────────┐    ┌──────────┐    ┌──────────┐                             │
│   │  Mobile  │    │   Web    │    │  Admin   │                             │
│   │   BFF    │    │   BFF    │    │   BFF    │                             │
│   │ ──────── │    │ ──────── │    │ ──────── │                             │
│   │ • Slim   │    │ • Rich   │    │ • Full   │                             │
│   │ • Cached │    │ • Fast   │    │ • Detailed│                            │
│   └────┬─────┘    └────┬─────┘    └────┬─────┘                             │
│        │               │               │                                    │
│        └───────────────┼───────────────┘                                   │
│                        │                                                    │
│                        ▼                                                    │
│              ┌─────────────────────────────────────┐                       │
│              │        MICROSERVICES                 │                       │
│              │  ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐   │                       │
│              │  │User │ │Order│ │Prod │ │Ship │   │                       │
│              │  └─────┘ └─────┘ └─────┘ └─────┘   │                       │
│              └─────────────────────────────────────┘                       │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Mobile BFF Implementation

// mobile-bff/src/routes/products.js
const express = require('express');
const router = express.Router();

// Mobile-optimized product list
router.get('/', async (req, res) => {
  const products = await productService.getProducts(req.query);
  
  // Return slim response for mobile
  res.json({
    products: products.map(p => ({
      id: p.id,
      name: p.name,
      price: p.price,
      thumbnail: p.images[0]?.thumbnail, // Only first thumbnail
      rating: p.averageRating,
      inStock: p.stockQuantity > 0
    })),
    pagination: products.pagination
  });
});

// Mobile-optimized product details
router.get('/:id', async (req, res) => {
  const [product, reviews] = await Promise.all([
    productService.getProduct(req.params.id),
    reviewService.getTopReviews(req.params.id, 3) // Only top 3 reviews
  ]);
  
  res.json({
    id: product.id,
    name: product.name,
    price: product.price,
    description: product.shortDescription, // Short description
    images: product.images.slice(0, 5), // Max 5 images
    rating: product.averageRating,
    reviewCount: product.reviewCount,
    topReviews: reviews,
    inStock: product.stockQuantity > 0,
    variants: product.variants.map(v => ({
      id: v.id,
      name: v.name,
      price: v.price,
      inStock: v.stockQuantity > 0
    }))
  });
});

Web BFF Implementation

// web-bff/src/routes/products.js
const express = require('express');
const router = express.Router();

// Web-optimized product list
router.get('/', async (req, res) => {
  const [products, categories, filters] = await Promise.all([
    productService.getProducts(req.query),
    categoryService.getCategories(),
    productService.getAvailableFilters(req.query)
  ]);
  
  // Rich response for web
  res.json({
    products: products.map(p => ({
      id: p.id,
      name: p.name,
      description: p.shortDescription,
      price: p.price,
      originalPrice: p.originalPrice,
      discount: p.discount,
      images: p.images,
      rating: p.averageRating,
      reviewCount: p.reviewCount,
      inStock: p.stockQuantity > 0,
      badges: p.badges,
      categories: p.categories
    })),
    pagination: products.pagination,
    categories,
    filters,
    meta: {
      totalProducts: products.total,
      executionTime: Date.now() - req.startTime
    }
  });
});

// Web-optimized product details
router.get('/:id', async (req, res) => {
  const [product, reviews, related, questions] = await Promise.all([
    productService.getProduct(req.params.id),
    reviewService.getReviews(req.params.id, { limit: 10 }),
    productService.getRelatedProducts(req.params.id, 8),
    questionService.getQuestions(req.params.id, { limit: 5 })
  ]);
  
  res.json({
    product: {
      ...product,
      specifications: product.specifications,
      fullDescription: product.fullDescription
    },
    reviews: {
      items: reviews.items,
      summary: reviews.summary,
      pagination: reviews.pagination
    },
    relatedProducts: related,
    questions: questions,
    breadcrumbs: await categoryService.getBreadcrumbs(product.categoryId)
  });
});

Circuit Breaker in Gateway

// gateway/src/middleware/circuitBreaker.js
const CircuitBreaker = require('opossum');

class GatewayCircuitBreaker {
  constructor() {
    this.breakers = new Map();
  }

  getBreaker(serviceName, options = {}) {
    if (this.breakers.has(serviceName)) {
      return this.breakers.get(serviceName);
    }

    const breaker = new CircuitBreaker(
      async (config) => {
        const response = await axios(config);
        return response;
      },
      {
        timeout: options.timeout || 10000,
        errorThresholdPercentage: options.errorThreshold || 50,
        resetTimeout: options.resetTimeout || 30000,
        volumeThreshold: options.volumeThreshold || 10,
        name: serviceName
      }
    );

    // Events
    breaker.on('open', () => {
      console.log(`Circuit OPEN for ${serviceName}`);
      this.alertService?.notify({
        type: 'circuit_open',
        service: serviceName,
        timestamp: new Date()
      });
    });

    breaker.on('halfOpen', () => {
      console.log(`Circuit HALF-OPEN for ${serviceName}`);
    });

    breaker.on('close', () => {
      console.log(`Circuit CLOSED for ${serviceName}`);
    });

    breaker.on('fallback', (result) => {
      console.log(`Fallback triggered for ${serviceName}`);
    });

    this.breakers.set(serviceName, breaker);
    return breaker;
  }

  async call(serviceName, config, fallback = null) {
    const breaker = this.getBreaker(serviceName);
    
    if (fallback) {
      breaker.fallback(fallback);
    }

    try {
      return await breaker.fire(config);
    } catch (error) {
      if (error.message.includes('Breaker is open')) {
        throw new ServiceUnavailableError(serviceName);
      }
      throw error;
    }
  }

  getStatus() {
    const status = {};
    for (const [name, breaker] of this.breakers) {
      status[name] = {
        state: breaker.opened ? 'OPEN' : breaker.halfOpen ? 'HALF_OPEN' : 'CLOSED',
        stats: breaker.stats
      };
    }
    return status;
  }
}

// Usage in route
router.get('/api/users/:id', async (req, res, next) => {
  try {
    const response = await circuitBreaker.call(
      'user-service',
      {
        method: 'get',
        url: `${services.users}/users/${req.params.id}`
      },
      // Fallback
      () => ({
        data: {
          id: req.params.id,
          name: 'Unknown User',
          _fallback: true
        }
      })
    );
    
    res.json(response.data);
  } catch (error) {
    next(error);
  }
});

// Circuit breaker status endpoint
router.get('/health/circuits', (req, res) => {
  res.json(circuitBreaker.getStatus());
});

Gateway with Kong

Kong is a popular open-source API Gateway.
# kong.yml - Declarative Configuration
_format_version: "3.0"

services:
  - name: user-service
    url: http://user-service:3001
    routes:
      - name: user-routes
        paths:
          - /api/users
        strip_path: false
    plugins:
      - name: rate-limiting
        config:
          minute: 100
          policy: redis
          redis_host: redis
      - name: jwt
        config:
          secret_is_base64: false
      - name: cors
        config:
          origins:
            - "*"
          methods:
            - GET
            - POST
            - PUT
            - DELETE

  - name: order-service
    url: http://order-service:3002
    routes:
      - name: order-routes
        paths:
          - /api/orders
    plugins:
      - name: rate-limiting
        config:
          minute: 50
      - name: request-transformer
        config:
          add:
            headers:
              - X-Gateway-Source:kong

  - name: product-service
    url: http://product-service:3003
    routes:
      - name: product-routes
        paths:
          - /api/products
    plugins:
      - name: proxy-cache
        config:
          strategy: memory
          content_type:
            - application/json
          cache_ttl: 300

# Global plugins
plugins:
  - name: correlation-id
    config:
      header_name: X-Correlation-ID
      generator: uuid
      
  - name: prometheus
    config:
      per_consumer: true
      
  - name: request-termination
    route: null
    service: null
    enabled: false  # Enable when needed
    config:
      status_code: 503
      message: "Service temporarily unavailable"
# docker-compose.yml for Kong
version: '3.8'

services:
  kong-database:
    image: postgres:13
    environment:
      POSTGRES_USER: kong
      POSTGRES_DB: kong
      POSTGRES_PASSWORD: kongpass
    volumes:
      - kong-data:/var/lib/postgresql/data

  kong-migration:
    image: kong:3.4
    command: kong migrations bootstrap
    depends_on:
      - kong-database
    environment:
      KONG_DATABASE: postgres
      KONG_PG_HOST: kong-database
      KONG_PG_PASSWORD: kongpass

  kong:
    image: kong:3.4
    depends_on:
      - kong-database
      - kong-migration
    environment:
      KONG_DATABASE: postgres
      KONG_PG_HOST: kong-database
      KONG_PG_PASSWORD: kongpass
      KONG_PROXY_ACCESS_LOG: /dev/stdout
      KONG_ADMIN_ACCESS_LOG: /dev/stdout
      KONG_PROXY_ERROR_LOG: /dev/stderr
      KONG_ADMIN_ERROR_LOG: /dev/stderr
      KONG_ADMIN_LISTEN: 0.0.0.0:8001
    ports:
      - "8000:8000"  # Proxy
      - "8001:8001"  # Admin API
      - "8443:8443"  # Proxy SSL
      - "8444:8444"  # Admin SSL

volumes:
  kong-data:

Interview Questions

Answer:
  1. Request Routing: Route requests to appropriate services
  2. Authentication/Authorization: Validate tokens, check permissions
  3. Rate Limiting: Protect services from overload
  4. Load Balancing: Distribute traffic across service instances
  5. Request/Response Transformation: Modify headers, body
  6. Caching: Cache responses for performance
  7. Monitoring/Logging: Centralized observability
  8. Circuit Breaking: Prevent cascade failures
  9. Protocol Translation: REST to gRPC, etc.
  10. Request Aggregation: Combine multiple service calls
Answer:BFF creates separate API gateways for different client types:Why:
  • Mobile needs slim responses (bandwidth)
  • Web needs rich responses (features)
  • Admin needs full data access
Benefits:
  • Optimized responses per client
  • Independent evolution
  • Better team ownership
  • Client-specific concerns
Drawbacks:
  • More services to maintain
  • Potential code duplication
  • Need for shared libraries
Answer:Common patterns:
  1. JWT Validation: Gateway validates token, forwards user info in headers
  2. OAuth 2.0: Gateway handles token introspection
  3. API Keys: Gateway validates keys, applies rate limits per key
  4. Session-based: Gateway manages sessions
Implementation:
  • Validate token at gateway
  • Extract user claims
  • Forward as headers to services (X-User-ID, X-User-Roles)
  • Services trust gateway (internal network)
  • Use mutual TLS between gateway and services
Answer:Strategies:
  1. Horizontal Scaling: Multiple gateway instances behind load balancer
  2. Stateless Design: No session state in gateway
  3. Efficient Routing: Use performant routing algorithms
  4. Caching: Cache static/semi-static responses
  5. Async Processing: Non-blocking I/O
  6. Connection Pooling: Reuse connections to services
Monitoring:
  • Track latency added by gateway
  • Monitor CPU/memory usage
  • Set up auto-scaling based on metrics
Alternatives:
  • Service mesh for internal traffic
  • Edge computing for some operations

Summary

Key Takeaways

  • API Gateway is the single entry point
  • Handles cross-cutting concerns centrally
  • BFF pattern optimizes for different clients
  • Circuit breakers prevent cascade failures
  • Kong/AWS API Gateway for production

Next Steps

In the next chapter, we’ll dive into Data Management patterns including database per service, sagas, and event sourcing.