Acumatica · Gateway

API Gateway Patterns — A Field Guide

API Gateway Patterns — A Field Guide is the work that makes the systems talk. The API is the contract between the producer and the consumer; the contract is what determines.

John Kihiu12 min read

An API gateway is the single front door that request traffic passes through before it reaches your services — the place to put the concerns that don't belong in any one service but every service needs: authentication, rate limiting, routing, and request/response transformation. Get the pattern right and it disappears into the infrastructure. Get it wrong and it becomes a second monolith, a shared chokepoint every team has to coordinate a deploy through.

Edge gateway vs. backend-for-frontend

The single biggest pattern decision is whether you run one shared gateway for all clients or a BFF (backend-for-frontend) per client type. A single edge gateway is simpler to operate — one set of routes, one auth policy, one place to look for logs — but it accumulates client-specific logic over time as the mobile team and the web team both need slightly different response shapes, and that logic tends to pile up in gateway configuration nobody owns. A BFF per client (one gateway for the mobile app, one for the web app, one for partner integrations) keeps that shaping logic close to the team that needs it, at the cost of running more gateway instances and duplicating the auth/rate-limit plumbing across them. Teams with a handful of well-differentiated client types tend to land on BFFs; teams with one dominant client type usually don't need the split.

Core cross-cutting responsibilities

The concerns that belong at the gateway are the ones that are identical across every backend service: TLS termination, authentication (validating a JWT or API key before the request reaches a service that shouldn't have to know how auth works), rate limiting, and request logging with a correlation ID injected on the way in. The concerns that don't belong at the gateway are business logic and anything that requires knowledge of a specific domain — a gateway that decides pricing rules or validates business invariants has stopped being infrastructure and started being an undocumented second implementation of your service.

The gateway should be replaceable without a migration project

If moving from Kong to Envoy (or vice versa) would require rewriting business logic, the gateway absorbed responsibilities it shouldn't have. A well-scoped gateway config is annoying to lose but never a multi-quarter migration.

Routing and aggregation patterns

Simple path-based routing (/orders/* to the orders service, /users/* to the users service) covers most cases and is easy to reason about. The pattern that gets more use than it should is request aggregation — the gateway fans a single client request out to two or three backend services and composes the response, saving a mobile client several round trips. This is genuinely useful for high-latency client networks, but it's also where gateways quietly grow business logic: composing three responses into one requires deciding what happens when one of the three fails, and that decision is domain logic wearing an infrastructure costume.

YAML · KONG ROUTE CONFIG
services:
  - name: orders-service
    url: http://orders.internal:8080
    routes:
      - name: orders-route
        paths: ["/api/orders"]
    plugins:
      - name: rate-limiting
        config:
          minute: 100
          policy: local
      - name: jwt
        config:
          claims_to_verify: ["exp"]

Rate limiting and circuit breaking at the edge

Rate limiting belongs at the gateway because it needs to happen before a request consumes any backend capacity — limiting inside the service means the expensive work (a DB connection, a query) already happened before you reject the request. The two common algorithms are token bucket (allows bursts up to a cap, then throttles to a steady rate) and sliding window (smoother, more accurate, slightly more expensive to compute). Circuit breaking is the complementary pattern: when a backend starts failing or timing out, the gateway stops sending it traffic for a cooldown period rather than piling retries onto a service that's already struggling.

A gateway outage is a total outage

Because every request passes through it, the gateway is the single point of failure for the entire API surface, even if every backend service is healthy. Run it across multiple availability zones and load-test its own capacity limits, not just the backends behind it.

When a gateway is the wrong tool

Not every cross-cutting concern belongs at the edge. Service-to-service traffic inside a cluster is usually better served by a service mesh sidecar than by routing everything back out through a central gateway — round-tripping internal calls through the edge adds latency and makes the gateway a bottleneck for traffic it was never meant to carry. The rule of thumb: gateway for north-south traffic (client to service), mesh or direct calls for east-west traffic (service to service).

PatternSolvesWatch for
Single edge gatewaySimple ops, one auth policyClient-specific logic piling up in config
BFF per client typeClient-tailored responsesDuplicated plumbing across BFFs
Request aggregationFewer round trips for slow clientsBusiness logic creeping into the gateway
Edge rate limitingProtects backend capacityNeeds to be zone-aware for accuracy

Wrapping up

A gateway earns its place by staying boring: routing, auth, rate limiting, and logging, applied uniformly, with nothing a service team would need to file a ticket to change. The moment it starts making domain decisions, it's no longer infrastructure — it's an undocumented service every other service now depends on.

John Kihiu
Acumatica ERP Developer · Laravel Engineer

Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.