TL;DR: An API gateway is a controlled entry point that routes client requests to backend services and applies shared policies such as authentication, rate limits, request size limits, and observability. Use it to simplify client access and centralize edge concerns, but keep business authorization and domain rules inside the owning service.
Why API gateways exist
As an application grows, clients should not need to know every internal service, port, deployment name, or database boundary. An API gateway presents a stable external surface while routing requests to the correct backend. It can terminate TLS, validate tokens, enforce quotas, transform protocols, and collect consistent telemetry.
The gateway is not automatically a microservices requirement. A modular monolith can use a gateway for edge security and routing, while a microservice system can fail if the gateway becomes a single overloaded business-logic monolith.
Gateway responsibilities
Useful gateway responsibilities include:
- routing by path, host, method, or version,
- TLS termination and security headers,
- authentication token verification,
- request size and content-type validation,
- rate limiting and quotas,
- request correlation and access logging,
- response compression and selective caching,
- controlled retries and circuit breaking,
- protocol translation at a clear boundary.
Keep domain decisions such as whether a user can refund an order or change a project role in the Orders or Projects service. The gateway can verify identity and pass trusted claims, but it should not become the only place where authorization exists.
Gateway versus reverse proxy and load balancer
A reverse proxy forwards traffic and may terminate TLS. A load balancer distributes traffic across healthy instances. An API gateway usually adds API-aware policy, identity, quotas, transformations, developer keys, and product-level observability.
The boundaries overlap. Nginx, Envoy, a cloud gateway, or a custom Node.js service can fill several roles. Choose the smallest capability that solves the problem. Adding a heavyweight gateway to one small service may create more configuration than value.
Route requests explicitly
Prefer explicit routes and ownership:
const routes = [
{ prefix: '/v1/auth', target: authService },
{ prefix: '/v1/orders', target: ordersService },
{ prefix: '/v1/users', target: usersService },
];
export async function route(req: Request) {
const match = routes.find((route) => req.path.startsWith(route.prefix));
if (!match) return response(404, { code: 'ROUTE_NOT_FOUND' });
return match.target.forward(req);
}
In production, route configuration should be versioned, reviewed, and validated before rollout. Avoid dynamic routing based on untrusted headers. If a route changes ownership, preserve compatibility or publish a migration plan.
Authentication and authorization
The gateway can validate an access token's issuer, audience, signature, expiry, and coarse scopes. It should attach a verified principal to the internal request through a trusted mechanism. Do not forward an arbitrary x-user-id supplied by a client.
Downstream services must still enforce resource ownership, tenant membership, and sensitive action permissions. A valid token means the caller is authenticated, not that every resource is accessible. Use service-to-service identity and restrict which internal services may call which routes.
For browser applications, combine the gateway with a secure session or token strategy. Our OAuth 2.0 and OIDC guide explains the identity layer, while the gateway should focus on verifying and forwarding the resulting principal.
Rate limiting and quotas
Rate limits protect capacity and make plans enforceable. Decide whether the limit is per IP, user, tenant, API key, route, or combination. A public login endpoint needs a different policy from an authenticated file download or an internal worker route.
Use a distributed counter when requests can reach multiple gateway instances. Return 429 Too Many Requests with a safe retry signal. Do not rely on an in-memory map in a horizontally scaled process.
For expensive operations, use a quota based on work units rather than requests. A report generation call, image processing job, or AI task may deserve more restrictive controls than a small read request.
Timeouts, retries, and circuit breakers
Set a total request deadline and pass a remaining budget downstream. A retry without a deadline can multiply load while users wait. Retry only operations known to be safe, usually idempotent reads or commands with an idempotency key.
Circuit breakers stop sending traffic to a failing dependency and allow recovery probes. Return a stable error contract to clients without exposing internal hostnames. For long-running operations, return a job identifier and let the client poll or subscribe to progress instead of holding a gateway request open indefinitely.
Observability
Generate a correlation ID at the edge if the client did not supply one, and propagate it to downstream services. Log method, route template, status, latency, response size, principal type, tenant, and upstream outcome. Redact authorization headers, cookies, personal data, and request bodies unless an approved diagnostic workflow requires them.
Track gateway metrics by route template rather than raw URL. Useful metrics include request rate, p50 and p95 latency, upstream latency, 4xx and 5xx rates, rate-limit responses, retries, circuit state, and connection errors. Distributed traces show whether the gateway or a downstream service is responsible for slow responses.
Caching and request transformation
Cache only safe, correctly scoped responses. A response containing tenant data must not be served to another tenant because a cache key forgot the tenant or authorization context. Use explicit cache-control headers and invalidate data when freshness matters.
Request transformation can help clients evolve, but it creates hidden contracts. Keep transformations documented and tested. Avoid silently changing business semantics at the edge. A gateway should adapt transport concerns, not disguise incompatible domain behavior.
High availability and deployment
Run multiple gateway instances across failure domains. Use health checks that verify the process and its critical dependencies without creating a thundering herd. Keep the gateway stateless where possible, storing rate-limit and session state in appropriate shared systems.
Roll out route and policy changes gradually. A configuration error can block every client, so validate in CI, use canary or weighted deployment, and keep a known-good configuration for rollback. Test gateway failure separately from backend failure.
Common mistakes
- Moving all business logic into one gateway.
- Trusting client-supplied identity headers.
- Retrying non-idempotent writes automatically.
- Using local rate-limit state across multiple instances.
- Caching private responses without tenant-aware keys.
- Logging bearer tokens or full request bodies.
- Treating a health endpoint that only checks process uptime as readiness.
Production checklist
- Routes and ownership are explicit and versioned.
- Token validation and service identity are configured.
- Domain authorization remains in owning services.
- Limits, quotas, timeouts, retries, and circuit breakers are tested.
- Cache keys include the correct identity and tenant scope.
- Logs, metrics, traces, and redaction are configured.
- Gateway instances are redundant and stateless where possible.
- Configuration changes support canary rollout and rollback.
Conclusion
An API gateway is valuable when it gives clients a stable boundary and gives operators a consistent place for edge policy. Keep it small, observable, and security-focused. SoftwareCrafting’s API design and architecture service can help choose between a managed gateway, reverse proxy, service mesh, or custom edge layer.

