What is API Composition?

API Composition

A deep, beginner-friendly walkthrough of the API Composition pattern — why microservices need it, how it works under the hood, and how companies like Netflix, Amazon, Uber, and Google use it at scale.

01
Introduction & History

One Screen, Many Services Behind It

API Composition is the microservices pattern that hides the complexity of “many independent backends” behind a single, unified response to the client.

Imagine you order a pizza online. The app shows you the price, the estimated delivery time, the driver’s name, and your loyalty points — all on one screen. That single screen almost certainly did not come from one place. Behind the scenes, a “pricing service” knows the price, a “logistics service” knows the delivery time, a “driver service” knows the driver’s name, and a “loyalty service” knows your points. Somebody has to go ask each of these services a question and glue the answers together into one neat response. That “someone” is API Composition.

API Composition is a design pattern used mainly in microservices architectures, where a single client request is fulfilled by calling multiple backend services, and then combining (“composing”) their individual responses into one unified result before sending it back to the client.

Simple Analogy

Think of a restaurant waiter. You do not walk into the kitchen and talk to the chef, the bartender, and the dessert cook separately. You tell the waiter what you want, the waiter visits each station, collects your food and drink, arranges everything nicely on a tray, and brings it all to your table at once. The waiter is doing “composition” — visiting multiple sources and combining the results into one delivery.

Where Did This Pattern Come From?

To understand why API Composition exists, we need a tiny bit of history. In the early 2000s, most large applications were built as monoliths — one big codebase, one big database, one deployable unit. If you wanted the price and the delivery time of an order, you simply ran one SQL query that joined a few tables in the same database. Easy.

Around 2011–2014, companies like Netflix, Amazon, and Uber began breaking their monoliths into microservices — small, independently deployable services, each owning its own private database. This solved a lot of problems (teams could move faster, services could scale independently, failures were isolated), but it created a brand new problem: data that used to live in one table now lives in five different databases, owned by five different teams, that cannot be joined with SQL anymore.

API Composition emerged directly as an answer to this gap. It became one of the standard patterns documented in microservices literature (notably popularized by Chris Richardson’s microservices.io pattern catalog) alongside close relatives like the API Gateway pattern and the Backend-for-Frontend (BFF) pattern.

1

Early 2000s — The Monolith Era

Single database, SQL joins solve everything. No composition needed.

2

2011–2014 — Rise of Microservices

Netflix, Amazon, and others split monoliths into independently owned services and databases.

3

~2014 — The “Database per Service” Problem

Cross-service joins become impossible. Teams need a way to combine data from multiple services.

4

2015–2018 — Pattern Formalized

API Composition, API Gateway, and BFF patterns get named and documented as standard solutions.

5

2018–Today — GraphQL & Federation

GraphQL and Apollo Federation offer a more declarative, standardized way to do composition at scale.

02
The Problem & Motivation

Why Microservices Need Composition in the First Place

Let’s make the problem concrete with our pizza-delivery example. Composition exists to solve a specific, sharp pain that only appears once you split a monolith into microservices.

In a monolith, getting an order’s full details might be one query:

SQL · the world before microservices
SELECT o.id, o.price, d.eta, u.name AS driver, l.points
FROM orders o
JOIN delivery d ON d.order_id = o.id
JOIN users u ON u.id = d.driver_id
JOIN loyalty l ON l.user_id = o.user_id
WHERE o.id = 42;

One database, one query, done. But once orders, delivery, users, and loyalty each live inside their own microservice with their own private database, this SQL simply cannot exist anymore — because a database cannot JOIN across a network boundary into someone else’s database. That is the core motivation.

!
Why not just let the client call all four services?

You could let the mobile app call the Order, Delivery, Driver, and Loyalty services directly and merge the answers itself. This is called “client-side composition,” and it sounds simple — but it means every mobile app, web app, and partner integration has to duplicate the same merging logic, handle four separate network failures, and expose internal service URLs to the public internet. This gets painful fast, especially on slow mobile networks.

What Problem Does API Composition Actually Solve?

Data

Fragmented Data

Related data is scattered across services that each own a private database, so no single query can retrieve it all.

Clients

Client Complexity

Without composition, every client (mobile, web, partner) must know about and call many internal services itself.

Network

Chatty Networks

Mobile clients making 5+ round trips over a weak connection leads to slow, unreliable user experiences.

Security

Leaky Internals

Exposing every microservice directly to the public internet increases the attack surface and couples clients to internal architecture.

API Composition solves all four by introducing a single component — the composer (often called an “API Composer” or “aggregator”) — that sits between the client and the microservices, does all the fan-out calling and merging work once, and gives the client one clean response.

03
Core Concepts

The Composition Vocabulary, in Plain English

Before going further, let’s define the vocabulary clearly. These terms recur through every remaining section, so getting them straight now pays off.

Role

API Composer

The component (a service, a gateway, or a piece of backend code) responsible for calling multiple downstream services and merging their responses. Also called the “aggregator.”

Role

Provider Service

Any of the underlying microservices that the composer calls to gather a piece of the data — e.g., the Order service or the Loyalty service.

Output

Composite Response

The single, unified object sent back to the client, built by combining data from all provider services.

Technique

Fan-out

The act of the composer sending requests out to multiple provider services at once (or in sequence).

Technique

Scatter-Gather

Another name for the same overall technique: “scatter” requests out to many services, then “gather” the results back together.

Failure

Partial Failure

When some provider services succeed and others fail or time out — a defining challenge of composition that does not exist with a single database query.

Client-Side vs Server-Side Composition

There are two flavors of this pattern, and understanding the difference matters a lot:

✓ Server-Side Composition

  • A backend component (API Gateway, BFF, or dedicated Composer service) does the fan-out and merging.
  • Client makes just one request.
  • Internal service topology stays hidden from the client.
  • Most common and generally recommended approach.

✗ Client-Side Composition

  • The client (browser, mobile app) itself calls multiple services directly.
  • Multiple round trips, especially painful on mobile networks.
  • Internal services exposed publicly, harder to secure and evolve.
  • Usually only reasonable for very simple or internal tools.

Why the Distinction Really Matters

The choice between client-side and server-side composition is not just a stylistic preference — it directly affects how easily you can evolve your microservice architecture. With server-side composition, you can split a service in two next quarter, rename an internal endpoint, or move it to a different region, and no external client ever notices; the composer absorbs the change. With client-side composition, every one of those internal moves risks breaking mobile app builds still in use by customers who have not upgraded, which is why nearly every mature production system converges on the server-side variant as its default posture.

04
Architecture & Components

Where the Composer Actually Sits

Let’s zoom out and look at where the composer physically sits in a typical microservices architecture.

Clientmobile / web app 1 request API Gatewaysingle entry point API ComposerFAN-OUT + MERGE 2a Order Serviceprivate DB 2b Delivery Serviceprivate DB 2c Driver Serviceprivate DB 2d Loyalty Serviceprivate DB 3 merged 4 response The composer sits behind the gateway, fanning out to independent services and merging their answers.
Fig 1 · Where the API Composer physically sits: behind the gateway, in front of the microservice fleet.

Notice the composer sits behind the API Gateway (sometimes the composer is logic embedded inside the gateway itself, sometimes it is a standalone service). Let’s break down each piece.

Entry

API Gateway

The single public entry point for all client traffic. Handles routing, authentication, rate limiting, and often hosts composition logic.

Brain

API Composer

The orchestrating brain: knows which provider services to call, in what order, and how to merge their fields into one response shape.

Backend

Provider Services

Independent microservices, each with a narrow responsibility and its own database, exposing a small API.

Discovery

Service Registry

A directory (like Eureka or Consul) that helps the composer find the current network address of each provider service.

Perf

Cache Layer

Sits between composer and providers (or in front of the composite response) to avoid re-fetching unchanged data on every call.

Resilience

Circuit Breaker

A safety mechanism that stops the composer from repeatedly hammering a provider service that is already failing.

Where Composition Logic Physically Lives

PlacementDescriptionWhen It Fits
Inside API GatewayComposition rules configured or coded directly in the gateway (e.g., Kong, Apigee plugins)Simple merges, small teams, few clients
Dedicated Composer ServiceA standalone backend service whose only job is orchestration and mergingComplex business logic, reused across many clients
Backend-for-Frontend (BFF)One composer per client type (mobile-BFF, web-BFF), tailored to that client’s exact needsVery different client needs (mobile vs. web vs. smartwatch)
GraphQL GatewayA GraphQL layer (e.g., Apollo Federation) declaratively stitches together data from multiple servicesLarge orgs with many teams and evolving query needs
05
Internal Working — Step by Step

What Actually Happens When a Request Arrives

Let’s trace exactly what happens, technically, when a composed request arrives — from the client’s single HTTP call to the merged JSON response coming back.

1

Request Arrives

The client sends GET /orders/42/summary to the API Gateway.

2

Composer Identifies Required Data

The composer inspects the request and determines it needs data from the Order, Delivery, Driver, and Loyalty services.

3

Fan-out (Scatter)

The composer sends requests to each provider — ideally in parallel, using async I/O or thread pools, not one after another.

4

Await Responses

The composer waits for all responses, applying a timeout per call so one slow service cannot stall everything forever.

5

Merge (Gather)

Successful responses are stitched into one object matching the shape the client expects.

6

Handle Partial Failures

If a non-critical service (e.g., Loyalty) failed or timed out, the composer may return the rest of the data with that field marked null or omitted, rather than failing the whole request.

7

Return Composite Response

One JSON object goes back to the client — it never knows four services were involved.

Java Example: A Simple Parallel Composer

Here is a simplified Java example using CompletableFuture to fan out requests in parallel and merge them — this is the heart of how a real-world composer works.

Java · a parallel fan-out composer
public class OrderSummaryComposer {

    private final OrderServiceClient orderClient;
    private final DeliveryServiceClient deliveryClient;
    private final DriverServiceClient driverClient;
    private final LoyaltyServiceClient loyaltyClient;

    public OrderSummary getOrderSummary(String orderId) {

        // 1. Fan-out: fire all calls in parallel, non-blocking
        CompletableFuture<Order> orderFuture =
            CompletableFuture.supplyAsync(() -> orderClient.getOrder(orderId));

        CompletableFuture<Delivery> deliveryFuture =
            CompletableFuture.supplyAsync(() -> deliveryClient.getDelivery(orderId));

        // Driver depends on delivery.getDriverId(), so it chains after delivery
        CompletableFuture<Driver> driverFuture = deliveryFuture.thenCompose(delivery ->
            CompletableFuture.supplyAsync(() -> driverClient.getDriver(delivery.getDriverId())));

        // Loyalty is "best effort" — allowed to fail without breaking the response
        CompletableFuture<Loyalty> loyaltyFuture =
            CompletableFuture.supplyAsync(() -> loyaltyClient.getPoints(orderId))
                .exceptionally(ex -> Loyalty.unavailable()); // fallback on failure

        // 2. Gather: wait for everything, with an overall timeout
        CompletableFuture.allOf(orderFuture, deliveryFuture, driverFuture, loyaltyFuture)
                .orTimeout(2, TimeUnit.SECONDS)
                .join();

        // 3. Merge into one composite response
        return new OrderSummary(
            orderFuture.join(),
            deliveryFuture.join(),
            driverFuture.join(),
            loyaltyFuture.join()
        );
    }
}
Why parallel, not sequential?

If each of the four calls takes 100 ms and you call them one after another (sequentially), the total wait is 400 ms. If you call them all at once (in parallel) and just wait for the slowest one, the total wait is roughly 100 ms — a 4x speedup. This is the single most important performance decision in a composer.

Dependency-Aware Fan-out

Notice one subtle detail in the code above: the Driver call does not run in the very first batch, because it needs the driver ID that only comes back inside the Delivery response. Real composers frequently have this shape — a mix of independent calls that can go in parallel from the start, plus one or two “dependent” calls that must chain after an earlier response. A well-written composer is really a small directed acyclic graph of calls, where every branch that can run in parallel does, and only the truly dependent hops wait for their upstream input.

06
Data Flow & Lifecycle

One Request, Many Fates

Let’s visualize the full lifecycle of a single composed request, including the tricky bit: what happens when one service fails while the others succeed.

Client API Gateway Composer Order Svc Delivery Svc Loyalty GET /orders/42/summary forward request PAR · Fan-out in parallel GET /orders/42 GET /deliveries?orderId=42 GET /loyalty/42 200 OK order 200 OK delivery timeout / 503 merge order + delivery, mark loyalty unavailable 200 OK composite JSON
Fig 2 · Full lifecycle of one composed request — including graceful degradation when the Loyalty service times out.

Notice that even though the Loyalty service failed, the overall request still succeeds — this is a deliberate design decision called graceful degradation, and it is one of the most important lifecycle behaviors of a well-built composer.

The Lifecycle of a Composite Field

  1. Request mapping — the composer decides which downstream calls the incoming request requires.
  2. Dispatch — calls go out, typically via HTTP/REST, gRPC, or message queues.
  3. Timeout & retry — each call has its own timeout budget and possibly a retry policy.
  4. Response validation — the composer checks each response is well-formed before using it.
  5. Merge / transform — fields are renamed, reshaped, or computed (e.g., converting cents to dollars) to match the client’s expected contract.
  6. Caching decision — the composer may cache the composite result for a short TTL to save future calls.
  7. Response delivery — the final JSON (or GraphQL) payload is returned to the client.
07
Advantages, Disadvantages & Trade-offs

What Composition Gives You — and What It Costs

API Composition is a high-leverage pattern, but it introduces its own operational surface area. Here is what you gain and what you pay.

✓ Advantages

  • Clients make one request instead of many — simpler client code, fewer round trips.
  • Internal microservice topology is hidden from the outside world.
  • Centralizes cross-cutting concerns like auth, caching, and error handling.
  • Provider services stay small, focused, and independently deployable.
  • Enables graceful degradation — partial data beats a total failure.

✗ Disadvantages

  • Adds an extra network hop and a new component to build and operate.
  • The composer can become a bottleneck or single point of failure if not built carefully.
  • Overall latency is bound by the slowest provider call in the fan-out.
  • Handling partial failures correctly adds real complexity.
  • Risk of the composer becoming a “mini-monolith” if too much business logic accumulates there.

Key Trade-offs to Think About

Trade-offChoice AChoice B
Call styleParallel (fast, more complex code)Sequential (simple, slower)
On partial failureReturn partial data (available, less complete)Fail the whole request (consistent, less resilient)
Where logic livesInside API Gateway (fewer moving parts)Dedicated Composer service (more scalable, more to operate)
Query flexibilityGraphQL federation (flexible, more setup cost)Fixed REST composite endpoint (simple, less flexible)
Composition trades one hard problem (impossible cross-service SQL joins) for a smaller, more manageable one (fan-out orchestration).
08
Performance & Scalability

Fast Composites Are Engineered, Not Accidental

Because a composed request depends on multiple downstream calls, performance needs deliberate engineering — it does not happen by accident.

The Golden Rule: Parallelize Independent Calls

As shown earlier, calls that do not depend on each other’s results should always run concurrently. Calls that do depend on a previous result (like needing the delivery record before you can look up the driver) must be chained, but everything else should fan out at once.

Timeout Budgets

A composer should set an overall deadline for the whole composite request (say, 2 seconds), and divide that budget sensibly across the calls it makes, rather than letting any single slow provider eat the whole budget.

4x
speedup, parallel vs sequential fan-out (4 calls @ 100 ms)
p99
composite latency ≈ max(p99 of each provider call)
< 5%
typical target overhead added by the composer itself

Scaling the Composer

  • Stateless design — composers should hold no session state, so you can run many identical instances behind a load balancer.
  • Connection pooling — reuse HTTP connections to downstream services instead of opening new ones per request.
  • Async I/O — non-blocking frameworks (like reactive Java with WebFlux, or Node.js) let one instance handle thousands of in-flight composite requests without needing a thread per request.
  • Caching — cache frequently requested, slow-changing composite results (e.g., product catalog data) to avoid unnecessary fan-out entirely.
!
The “Slowest Link” Trap

A common mistake is not noticing that your composite endpoint’s latency is entirely dictated by your slowest downstream service. If Loyalty occasionally takes 3 seconds, every single composite request that includes loyalty data will feel slow — even though Order and Delivery respond in 50 ms. Aggressive per-call timeouts and fallbacks are essential.

Batching and Coalescing Requests

A subtler performance technique is request coalescing: if the composer receives many concurrent requests that all need the same downstream data (for example, hundreds of users viewing the same popular product page in the same second), a well-tuned composer can consolidate them into a single downstream fetch and share the result across every waiting request, rather than firing one identical downstream call per incoming user request. Combined with a small in-memory cache, this can turn a thundering herd of duplicate downstream calls into one, which is enormously helpful at scale.

09
High Availability & Reliability

Composition Introduces More Places to Fail

Because composition introduces more moving parts (and more network calls) than a single database query, reliability has to be designed in explicitly.

Circuit Breakers

A circuit breaker is like an electrical fuse: if a provider service starts failing repeatedly, the circuit breaker “trips” and the composer stops calling it for a while (returning a fast fallback instead), giving the struggling service room to recover instead of being bombarded further.

Closednormal traffic flows failure rate exceeds threshold Opencalls short-circuit, fallback returned after cooldown Half-Opentest call test succeeds → back to Closed test fails → back to Open The three states of a circuit breaker, and the transitions between them.
Fig 3 · Circuit breaker state machine — a small state chart, an outsized impact on system reliability.

Bulkheads

A bulkhead (borrowed from ship design, where separate watertight compartments stop one flooded section from sinking the whole ship) isolates resources per downstream call — for example, using a separate thread pool for calls to the Loyalty service, so if Loyalty gets slow, it cannot exhaust the threads needed to call Order and Delivery.

Retries with Backoff

Transient failures (a brief network blip) can be retried, but retries must use exponential backoff with jitter — waiting slightly longer between each retry, with some randomness — to avoid a “retry storm” where every client retries at exactly the same moment and overwhelms an already-struggling service.

✓ Reliability Techniques

  • Circuit breakers per downstream dependency
  • Bulkheaded thread/connection pools
  • Sensible per-call and overall timeouts
  • Fallback values for non-critical fields
  • Health checks and readiness probes

✗ Failure Modes to Guard Against

  • Cascading failures (one slow service takes down the whole composer)
  • Thread pool exhaustion from one misbehaving dependency
  • Retry storms amplifying an outage
  • Composer itself becoming a single point of failure
10
Security

A Composer Sits in a Privileged Position

The composer sits in a privileged position — it talks to many internal services on behalf of the client — so it needs careful security treatment.

  • Authentication propagation — the composer typically validates the client’s token (e.g., a JWT) once at the gateway, then forwards a trusted internal token or context to downstream services, rather than making every provider re-validate the raw client credential.
  • Least privilege — each provider service should only expose the fields the composer actually needs, not entire internal records.
  • Field-level authorization — the composer must be careful not to accidentally merge sensitive fields (like another user’s data) into a response just because a downstream call returned them.
  • mTLS between services — mutual TLS ensures the composer and provider services trust each other’s identity on the internal network.
  • Rate limiting — since one client request can fan out into several backend calls, a small client-side attack can amplify into a much larger load on internal services; rate limiting at the gateway protects against this.
!
Over-fetching Risk

Because the composer touches multiple services, it is easy to accidentally include more data in the composite response than the client should see — for example, including an internal risk score or another user’s private notes. Always define an explicit, reviewed response contract rather than just merging whatever the providers return.

Amplification and Abuse

Because a composite endpoint quietly turns one incoming request into several backend calls, it also silently amplifies whatever load hits it. That is fine for legitimate traffic, but it is also an attractive target for someone trying to abuse the system: a request that costs an attacker 1 ms of network effort can trigger 5 or 10 backend calls, each with its own database query. Composers therefore usually pair per-user rate limits at the gateway with per-dependency circuit breakers, so a burst of malicious traffic cannot silently become a burst of expensive downstream work.

11
Monitoring, Logging & Metrics

Which Downstream Call Made This Slow?

Because a single client request now touches several services, observability has to be able to answer: “which downstream call made this request slow or broken?”

Distributed Tracing

Each incoming request is tagged with a unique trace ID that is passed along to every downstream call. Tools like Jaeger, Zipkin, or OpenTelemetry let you visualize the entire fan-out as one connected trace, showing exactly how long each provider call took — making it trivial to spot which particular hop is dragging down the composite latency for a given request.

Latency

Per-call latency

Track p50/p95/p99 latency for each downstream dependency separately, not just the overall composite endpoint.

Errors

Error rate per dependency

A spike in Loyalty service errors should be visible on its own, not hidden inside an aggregate “5% error rate.”

Resilience

Circuit breaker state

Dashboards should show which circuits are open, half-open, or closed in real time.

Degradation

Partial failure rate

Track how often the composite response is returned with missing/degraded fields — a leading indicator of downstream trouble.

Structured Logging

Logs from the composer should include the trace ID, the target service, response time, and status for every downstream call — this makes it possible to reconstruct exactly what happened for any given failed or slow request after the fact.

Alerting on the Right Things

A subtle but important observability lesson for composers: alert on per-dependency SLOs rather than only on the aggregate composite endpoint’s SLO. A composite endpoint may still meet its overall latency target while one specific dependency is quietly degrading enough to cause partial failures on a growing fraction of requests. Separate per-dependency dashboards and alerts catch that class of slow-motion incident well before the aggregate metric would ever look bad enough to page someone.

12
Deployment & Cloud

How Composers Actually Ship

API Composers are typically deployed the same way as any other microservice: as containerized applications (Docker) orchestrated by Kubernetes, often sitting right behind a managed API Gateway.

Runtime

Containers & Kubernetes

The composer runs as a stateless Deployment with multiple replicas, scaled horizontally based on CPU or request-rate metrics.

Managed

Managed API Gateways

Cloud services like AWS API Gateway, Apigee, or Kong can host lightweight composition/aggregation logic directly.

Serverless

Serverless Composition

AWS Lambda or similar functions can act as lightweight composers for simpler aggregation needs, scaling automatically with traffic.

Mesh

Service Mesh

Tools like Istio or Linkerd can handle retries, timeouts, and mTLS between the composer and providers transparently.

Rolling Out Changes Safely

Because the composer depends on the contracts of multiple provider services, deployments should use canary releases or blue-green deployments, along with contract testing (e.g., Pact) to catch breaking changes in provider APIs before they reach production.

Multi-Region Considerations

For globally distributed applications, deploying composers close to their users (in multiple regions) dramatically reduces perceived latency — but it also means each regional composer instance needs local, low-latency access to its downstream services (or fast read replicas thereof), because a composer in Singapore calling providers only in Virginia will still be as slow as any other cross-region round trip. Regional composers therefore typically pair with regional deployments (or at least regional caches) of the provider services they call most.

13
Databases, Caching & Load Balancing

The Composer Never Touches a Database Directly

Composition sits carefully above the data layer: it talks only to service APIs, never to their databases directly. Here is how that plays out end-to-end.

Databases

A defining feature of the microservices world that necessitates composition is the database-per-service pattern — each provider service owns its own database (SQL or NoSQL) and never lets another service query it directly. The composer never touches a database directly; it only talks to service APIs.

Caching

Caching is one of the most effective performance levers for a composer:

  • Per-provider caching — cache individual downstream responses (e.g., product details that rarely change) to skip redundant calls.
  • Composite response caching — cache the fully merged result for a short TTL (e.g., 30 seconds) when slight staleness is acceptable.
  • Cache invalidation — providers can publish change events (via a message broker) that tell the cache layer to invalidate stale composite data.

Load Balancing

Both the client-to-composer hop and the composer-to-provider hops need load balancing:

LayerTypical Technique
Client → ComposerL7 load balancer / API Gateway distributing across composer replicas
Composer → ProviderClient-side load balancing (via service registry) or a service mesh sidecar proxy
14
APIs & Microservices

Composition Across REST, GraphQL, gRPC and BFFs

API Composition is deeply tied to how microservices expose their APIs. Let’s look at how it plays with the common API styles.

REST

REST Composition

Composer calls several REST endpoints and manually merges JSON responses into a new composite JSON shape.

GraphQL

GraphQL Federation

Each service exposes a “subgraph”; a gateway (e.g., Apollo Router) stitches them into one federated schema automatically per query.

gRPC

gRPC Composition

Composer uses gRPC stubs to call multiple services efficiently over HTTP/2, useful for internal high-throughput composition.

BFF

BFF (Backend for Frontend)

A specialized composer built for one specific client (e.g., mobile app), shaping data exactly the way that client needs it.

API Composition vs. API Gateway vs. BFF — What’s the Difference?

PatternMain Job
API GatewaySingle entry point: routing, auth, rate limiting — may also do composition
API CompositionSpecifically: calling multiple services and merging their data into one response
Backend for FrontendA dedicated composer tailored per client type, often built using the composition pattern internally
An API Gateway is where composition often lives; API Composition is what actually happens.
15
Design Patterns & Anti-patterns

Shapes That Work — and Shapes That Don’t

API Composition rarely lives in isolation — it lines up next to a handful of closely related patterns, and it has its own set of recognizable anti-patterns.

Related Design Patterns

Messaging

Scatter-Gather

The classic messaging pattern of sending a request to many recipients and aggregating their replies — the technical foundation of composition.

Read Model

CQRS + Materialized View

An alternative to real-time composition: pre-build and store a denormalized “read model” combining data from multiple services ahead of time.

Writes

Saga Pattern

Handles multi-service writes (as opposed to composition, which handles multi-service reads) using a sequence of local transactions.

Resilience

Bulkhead & Circuit Breaker

Resilience patterns commonly paired with composition to isolate and protect against downstream failures.

Anti-patterns to Avoid

!
The “God Composer”

Letting one composer accumulate so much business logic and so many downstream dependencies that it becomes a new monolith in disguise. Keep composers thin — orchestration and shaping, not business rules.

!
Synchronous Chains of Composers

A composer calling another composer, which calls another composer, creates deep synchronous call chains where a failure or slowdown anywhere cascades all the way up, and latency stacks up badly.

!
No Fallback Strategy

Treating every downstream call as equally critical, so that one minor service being down (like a “recently viewed” recommendation service) takes down an entire page.

16
Best Practices & Common Mistakes

Habits That Keep Composers Fast, Safe, and Sane

A distilled checklist of the practices that make composers pleasant to operate — and the mistakes that quietly turn them into pager-driven nightmares.

✓ Best Practices

  • Parallelize independent downstream calls whenever possible.
  • Set explicit timeouts on every call, plus an overall request deadline.
  • Classify each dependency as “critical” or “best-effort” and design fallbacks accordingly.
  • Use circuit breakers and bulkheads per dependency.
  • Keep composers stateless and horizontally scalable.
  • Instrument every downstream call with tracing and per-dependency metrics.
  • Cache aggressively where staleness is acceptable.

✗ Common Mistakes

  • Calling downstream services sequentially when they could run in parallel.
  • No timeout at all — one hung service can hang every composite request.
  • Treating every downstream failure as a total failure of the request.
  • Letting the composer accumulate business logic that belongs in a provider service.
  • Skipping contract tests, so a provider’s API change silently breaks the composer.
  • Forgetting to cache, causing unnecessary repeated load on providers.

A Sensible Rollout Path for a New Composite Endpoint

1

Nail the composite contract first

Agree on the exact response shape with the client team before writing any fan-out code — changing this later is expensive.

2

Classify each dependency

For every provider call, decide up front whether it is critical or best-effort, and what the fallback value looks like if it is unavailable.

3

Wire up observability before turning it on

Per-dependency latency, error rates, and circuit breaker state should all be visible from the very first request.

4

Load-test with realistic fan-out patterns

Because one composite request may become several downstream calls, load-testing the composite endpoint tells you as much about the backend fleet as it does about the composer itself.

17
Real-World / Industry Examples

Where You Have Already Been Using Composition

You have almost certainly interacted with API Composition already today — you just did not see it. Here are some places where it hides in plain sight.

Streaming

Netflix

Netflix’s API Gateway layer (historically built on Zuul, and the well-known “API Gateway” pattern write-ups from their engineering blog) composes data from dozens of backend microservices to build a single personalized home-screen response per device type.

E-commerce

Amazon

A single Amazon product page composes data from pricing, inventory, reviews, recommendations, and shipping-estimate services — each independently owned and scaled.

Ride-hailing

Uber

A single trip-summary screen composes data from the trip service, pricing/fare service, driver service, and ratings service into one rider-facing view.

Search

Google

Search result pages and many Google product surfaces compose “knowledge panels,” ads, and organic results from many independent backend systems in real time.

Across all of these, the underlying motivation is identical to our pizza-delivery example: independently owned, independently scaled services, with a composition layer that gives the end user (or the mobile client) one fast, unified answer — regardless of how many separate teams and databases actually contributed to it behind the scenes.

18
FAQ

Questions That Come Up Every Time This Pattern Is Discussed

A quick round of the questions that get asked most often when a team is deciding whether — or how — to introduce API Composition.

Is API Composition the same as an API Gateway?

No, but they are closely related. An API Gateway is the single entry point for all client traffic and can host many responsibilities (auth, rate limiting, routing). API Composition is a specific pattern — often implemented inside the gateway, or as a separate service behind it — for combining data from multiple backend services into one response.

Does API Composition replace a shared database?

Not exactly — it is a workaround for not having one. In a monolith with a single database, you would use SQL joins. In microservices, each service owns its data privately, so composition is how you reconstruct a “joined” view across service boundaries at request time.

Is GraphQL the same thing as API Composition?

GraphQL (especially with schema federation) is one popular implementation approach for composition — it lets you declare a unified schema across services and have a gateway resolve fields from the right subgraph automatically. But you can absolutely do API Composition with plain REST and hand-written merging code too.

What happens if one of the services is down?

A well-designed composer applies fallback logic: critical data may cause the whole request to fail, while non-critical data is simply omitted or marked unavailable so the rest of the response still succeeds — this is called graceful degradation.

When should I NOT use API Composition?

If your application is still a monolith with one shared database, you do not need this pattern — a normal query is simpler and faster. It is specifically valuable once you have split into microservices with separate databases and a client needs data that spans more than one of them.

Can composition happen for writes, not just reads?

Composition is primarily a read-side pattern. Coordinating writes across multiple services is a different, harder problem usually solved with the Saga pattern instead.

How is composition different from caching a joined view up front?

Materialized views (a common alternative under the CQRS umbrella) pre-compute the joined data ahead of time and store it in its own read store, so requests read it directly without any real-time fan-out. That is often faster at read time, but it introduces its own operational cost: keeping the materialized view fresh as source data changes. Composition trades that up-front cost for a small per-request fan-out cost. Many production systems end up using both — a materialized view for the hottest, cheapest-to-precompute reads, and live composition for everything else.

19
Summary & Key Takeaways

One Pattern to Rebuild the Joins You Gave Up

API Composition is the pattern that lets a microservices architecture feel simple from the outside, even though it is complex on the inside.

It solves the very real problem created by splitting a monolith’s shared database into many independently owned databases: related data ends up scattered, and something has to put it back together for the client. That “something” is the composer — a thin, deliberately narrow layer whose whole job is fan-out and merge.

Key Takeaways

  • What it is: a component that fans out requests to multiple provider services and merges their responses into one composite response.
  • Why it exists: database-per-service microservices make cross-service SQL joins impossible, so composition rebuilds that “joined” view at request time.
  • How it works: parallel fan-out, per-call timeouts, merging, and graceful handling of partial failures.
  • Where it lives: inside an API Gateway, a dedicated composer/BFF service, or a GraphQL federation layer.
  • What makes it reliable: circuit breakers, bulkheads, retries with backoff, and clear critical vs. best-effort classification of dependencies.
  • What makes it fast: parallel calls, caching, and stateless horizontal scaling.
  • Where it is used: Netflix, Amazon, Uber, and Google all rely on composition to build single-screen experiences from dozens of independent backend services.
i
Summary in One Sentence

API Composition is how modern microservice architectures give clients one simple response built from many independent services — parallel fan-out, careful merging, and deliberate graceful degradation on partial failures.

Once you see this pattern, you will notice it everywhere — nearly every modern app screen you use is quietly the product of an API Composer working behind the scenes, gathering pieces from independent teams and independent databases into the single, unified answer you actually see.