API Gateway

Intro

An API Gateway is a single entry point between external clients and a set of backend services. It centralizes cross-cutting concerns such as request routing, authentication and authorization enforcement, rate limiting, TLS termination, and traffic policies so individual services do not have to re-implement them. This matters because it gives you one place to enforce consistency and security while keeping clients simpler, especially when each client would otherwise need to call many services directly. You reach for it when you have microservices with multiple consumer types, or when you want a BFF approach where each client family gets an API surface tailored to its needs.

In .NET ecosystems, a common implementation is to run a reverse proxy gateway at the system edge and keep service-level business behavior inside domain services.

Core Responsibilities

flowchart LR
    Client[Client Apps] --> Gateway[API Gateway]
    Gateway --> SvcA[Service A]
    Gateway --> SvcB[Service B]
    Gateway --> SvcC[Service C]

Patterns

Gateway Routing

Use the gateway as the policy and routing edge. Clients call one host, and route rules dispatch traffic to internal services.

When it works best:

Gateway Aggregation

The gateway composes a single response from multiple service calls to reduce client round trips.

Concrete example:

Use carefully: aggregation is orchestration logic, not domain logic. Keep it thin and response-oriented.

Gateway Offloading

The gateway handles edge concerns such as TLS, compression, CORS, header normalization, and request size limits.

Benefit:

BFF (Backend for Frontend)

Separate gateways or gateway routes per client type (web, mobile, partner API) when each has different payload, latency, or auth requirements.

Why this is useful:

.NET Implementation (YARP)

For .NET, YARP (Yet Another Reverse Proxy) is a Microsoft-maintained reverse proxy library (Yarp.ReverseProxy) that you can use as the core of an API gateway.

Minimal appsettings.json routing and cluster example:

{
  "ReverseProxy": {
    "Routes": {
      "orders-route": {
        "ClusterId": "orders-cluster",
        "Match": {
          "Path": "/api/orders/{**catch-all}"
        }
      },
      "catalog-route": {
        "ClusterId": "catalog-cluster",
        "Match": {
          "Path": "/api/catalog/{**catch-all}"
        }
      }
    },
    "Clusters": {
      "orders-cluster": {
        "Destinations": {
          "orders-d1": {
            "Address": "https://orders-service.internal/"
          }
        }
      },
      "catalog-cluster": {
        "Destinations": {
          "catalog-d1": {
            "Address": "https://catalog-service.internal/"
          }
        }
      }
    }
  }
}

Minimal registration in ASP.NET Core:

var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));

var app = builder.Build();

app.MapReverseProxy();

app.Run();

YARP composes well with ASP.NET Core middleware and observability tooling. Ocelot is a known alternative and can be a pragmatic fit in teams already invested in its ecosystem.

Gateway vs Service Mesh

API Gateway and Service Mesh solve different traffic planes and are often used together.

Rule of thumb:

Tradeoffs

Pitfalls

  1. Gateway becomes a monolith bottleneck

    • What goes wrong: every change flows through one oversized gateway, and outages impact all consumers.
    • Why it happens: uncontrolled feature growth and weak horizontal scaling strategy.
    • How to prevent/detect: keep gateway stateless, scale out aggressively, split by bounded context or BFF when ownership and traffic diverge.
  2. Business logic creeps into the gateway

    • What goes wrong: domain rules are duplicated at the edge, causing inconsistent behavior and hard-to-test flows.
    • Why it happens: aggregation code gradually turns into orchestration and then decision logic.
    • How to prevent/detect: enforce a boundary rule that gateway owns transport and policy only; domain invariants stay in services.
  3. Extra latency from the additional hop

    • What goes wrong: p95 and p99 latency increase, especially under fan-out aggregation.
    • Why it happens: more network hops, serialization work, and downstream dependency chains.
    • How to prevent/detect: measure end-to-end traces, cap fan-out depth, use parallel downstream calls, and cache only where freshness allows.
  4. Configuration sprawl with many routes

    • What goes wrong: route conflicts, accidental exposure, and hard-to-review config changes.
    • Why it happens: rapid service growth without governance for route naming and ownership.
    • How to prevent/detect: define route conventions, enforce config validation in CI, and assign clear ownership per route group.

Questions

References


Whats next