Skip to main content
The API Gateway is a Node.js/Express service that acts as the single entry point for all client requests to the QeetMart microservices architecture. It handles request routing, authentication, rate limiting, and CORS.

Overview

The gateway proxies requests to downstream services and provides cross-cutting concerns like security, logging, and service health monitoring.
The API Gateway runs on port 4000 by default and routes requests to all backend microservices.

Technology stack

  • Runtime: Node.js
  • Framework: Express 4.21.1
  • Language: TypeScript
  • Key dependencies:
    • http-proxy-middleware - Request proxying
    • express-rate-limit - Rate limiting
    • helmet - Security headers
    • cors - CORS handling
    • zod - Schema validation

Configuration

Environment variables

PORT
number
default:"4000"
Server port
HOST
string
default:"0.0.0.0"
Server host address
TRUST_PROXY
boolean
default:"false"
Enable when deployed behind a reverse proxy
CORS_ORIGINS
string
default:"http://localhost:3000,..."
Comma-separated list of allowed origins
CORS_CREDENTIALS
boolean
default:"true"
Allow credentials in CORS requests
REQUIRE_AUTH
boolean
default:"true"
Enforce JWT authentication on all routes
JWT_SECRET
string
required
Secret key for JWT verification (must match auth-service)
JWT_ISSUER
string
default:"http://localhost:4001"
Expected JWT issuer URI
JWT_AUDIENCE
string
Expected JWT audience (optional)
RATE_LIMIT_WINDOW_MS
number
default:"60000"
Rate limit window in milliseconds
RATE_LIMIT_MAX
number
default:"1000"
Maximum requests per window
GATEWAY_PROXY_TIMEOUT_MS
number
default:"5000"
Proxy request timeout in milliseconds

Downstream service URLs

AUTH_SERVICE_URL
string
default:"http://localhost:4001"
Auth service base URL
USER_SERVICE_URL
string
default:"http://localhost:8082"
User service base URL
PRODUCT_SERVICE_URL
string
default:"http://localhost:8083"
Product service base URL
ORDER_SERVICE_URL
string
default:"http://localhost:8084"
Order service base URL
PAYMENT_SERVICE_URL
string
default:"http://localhost:8085"
Payment service base URL
INVENTORY_SERVICE_URL
string
default:"http://localhost:8080"
Inventory service base URL

API endpoints

Health check

curl http://localhost:4000/health

Service health check

curl http://localhost:4000/health/services

Gateway info

curl http://localhost:4000/info

Route mapping

The gateway maps API paths to downstream services:
Client PathDownstream ServiceUpstream Path
/api/v1/auth/*auth-service/auth/*
/api/v1/users/*user-service/users/*
/api/v1/products/*product-service/products/*
/api/v1/orders/*order-service/orders/*
/api/v1/payments/*payment-service/payments/*
/api/v1/inventory/*inventory-service/inventory/*

Authentication

The gateway validates JWT tokens on all /api/* routes when REQUIRE_AUTH=true.
import { Request, Response, NextFunction } from 'express';
import { verifyJwt } from '../utils/jwt.js';
import { gatewayConfig } from '../config/env.js';

export const authMiddleware = (
  req: Request,
  res: Response,
  next: NextFunction
) => {
  // Skip auth for public routes
  if (!req.path.startsWith('/api/')) {
    return next();
  }

  if (!gatewayConfig.requireAuth) {
    return next();
  }

  const authHeader = req.headers.authorization;
  const token = authHeader?.startsWith('Bearer ')
    ? authHeader.substring(7)
    : null;

  if (!token) {
    return res.status(401).json({
      success: false,
      error: {
        message: 'Authentication required',
        code: 'UNAUTHORIZED'
      }
    });
  }

  try {
    const payload = verifyJwt(token);
    req.token = token;
    req.userId = payload.sub;
    next();
  } catch (error) {
    return res.status(401).json({
      success: false,
      error: {
        message: 'Invalid or expired token',
        code: 'UNAUTHORIZED'
      }
    });
  }
};

Request forwarding

The gateway forwards authentication tokens and correlation IDs to downstream services:
proxyReq.setHeader('authorization', `Bearer ${req.token}`);
proxyReq.setHeader('x-correlation-id', req.correlationId);
proxyReq.setHeader('x-request-id', req.correlationId);

Rate limiting

Rate limiting is applied to all /api/* routes:
const limiter = rateLimit({
  windowMs: 60000,        // 1 minute
  max: 1000,              // 1000 requests per window
  standardHeaders: true,
  legacyHeaders: false,
});
app.use('/api/', limiter);
Rate limit exceeded responses return HTTP 429 with the message configured in RATE_LIMIT_MESSAGE.

Running the service

cd api-gateway
pnpm install
pnpm dev

Error handling

The gateway returns consistent error responses:
{
  "success": false,
  "error": {
    "message": "Service user-service is unavailable",
    "code": "SERVICE_UNAVAILABLE",
    "correlationId": "abc-123-def"
  }
}

Dependencies

The gateway depends on:
  • All downstream microservices for proxying
  • Auth service for JWT secret coordination

Source code

Location: ~/workspace/source/micros/api-gateway/ Key files:
  • src/index.ts - Main application entry point
  • src/routes/gateway.routes.ts - Proxy route configuration
  • src/config/services.ts - Service registry
  • src/middleware/auth.middleware.ts - JWT authentication

Build docs developers (and LLMs) love