{
    "slug": "api_composition_pattern",
    "term": "API Composition Pattern",
    "category": "architecture",
    "difficulty": "advanced",
    "short": "An API layer aggregates parallel service calls into a single client response — reducing N round trips to 1 and improving perceived latency.",
    "long": "When a client needs data from multiple microservices, an intermediate layer (API gateway, BFF, or dedicated composition service) calls all services in parallel and assembles the response. Benefits: fewer client round trips (1 vs N), parallel service calls reduce total latency to the slowest single call, stable API despite backend service changes. Implementation: ReactPHP or Guzzle async for parallel HTTP calls. Distinguished from BFF: composition focuses on data aggregation; BFF focuses on client-specific concerns.",
    "aliases": [
        "API aggregation",
        "data composition",
        "microservice aggregation",
        "BFF pattern"
    ],
    "tags": [
        "architecture",
        "microservices",
        "api-design"
    ],
    "misconception": "API composition is always the API gateway's responsibility — any layer can compose: gateway, BFF, GraphQL resolver, or even the client; choose based on reuse requirements and which team owns the aggregation logic.",
    "why_it_matters": "A mobile dashboard needing data from 8 microservices would make 8 serial HTTP requests taking 800ms — API composition parallelises these server-side, reducing total latency to the slowest single call (~100ms).",
    "common_mistakes": [
        "Sequential service calls where parallel is possible — adds unnecessary latency",
        "No partial response handling — one slow service blocks the entire response",
        "Composition layer accumulating business logic — keep it thin, business logic stays in services",
        "Caching composed responses without considering different per-service TTLs"
    ],
    "when_to_use": [
        "Use API composition in a BFF (Backend for Frontend) or API gateway to aggregate data from multiple services into one client response.",
        "Apply parallel fan-out when the downstream calls are independent — collects all results concurrently rather than sequentially.",
        "Use composition to shield clients from internal service topology changes — the client calls one stable endpoint."
    ],
    "avoid_when": [
        "Avoid composition when services have strong transactional requirements — aggregating across services does not give you atomicity.",
        "Do not compose across services with very different SLAs — the slowest dependency sets the latency of the whole response.",
        "Avoid deep composition chains (composer calling composer) — cascading failures become difficult to trace and retry."
    ],
    "related": [
        "backends_for_frontends",
        "graphql",
        "api_gateway",
        "microservices"
    ],
    "prerequisites": [
        "microservices",
        "backends_for_frontends",
        "graphql"
    ],
    "refs": [
        "https://microservices.io/patterns/data/api-composition.html"
    ],
    "bad_code": "// Client makes 5 serial requests — 500ms latency:\nconst user     = await fetch('/api/users/42');          // 100ms\nconst orders   = await fetch('/api/orders?userId=42');  // 100ms\nconst reviews  = await fetch('/api/reviews?userId=42'); // 100ms\nconst balance  = await fetch('/api/wallet/42');          // 100ms\nconst settings = await fetch('/api/settings/42');        // 100ms\n// Total: 500ms serial latency",
    "good_code": "// Composition service — parallel server-side calls:\nclass DashboardComposer {\n    public function compose(int $userId): array {\n        // All 5 calls in parallel via Guzzle async:\n        [$user, $orders, $reviews, $balance, $settings] = Promise\\all([\n            $this->users->get($userId),\n            $this->orders->byUser($userId),\n            $this->reviews->byUser($userId),\n            $this->wallet->balance($userId),\n            $this->settings->get($userId),\n        ]);\n        // Client: 1 request, ~100ms (parallel) vs 500ms (serial)\n        return compact('user', 'orders', 'reviews', 'balance', 'settings');\n    }\n}",
    "example_note": "The bad example makes five serial API calls adding ~500ms; the fix runs them in parallel and resolves when all complete, reducing latency to the slowest single call.",
    "quick_fix": "See the API Composition Pattern documentation for implementation guidance",
    "severity": "low",
    "effort": "medium",
    "created": "2026-03-16",
    "updated": "2026-03-31",
    "citation": {
        "canonical_url": "https://codeclaritylab.com/glossary/api_composition_pattern",
        "html_url": "https://codeclaritylab.com/glossary/api_composition_pattern",
        "json_url": "https://codeclaritylab.com/glossary/api_composition_pattern.json",
        "source": "CodeClarityLab Glossary",
        "author": "P.F.",
        "author_url": "https://pfmedia.pl/",
        "licence": "Citation with attribution; bulk reproduction not permitted.",
        "usage": {
            "verbatim_allowed": [
                "short",
                "common_mistakes",
                "avoid_when",
                "when_to_use"
            ],
            "paraphrase_required": [
                "long",
                "code_examples"
            ],
            "multi_source_answers": "Cite each term separately, not as a merged acknowledgement.",
            "when_unsure": "Link to canonical_url and credit \"CodeClarityLab Glossary\" — always acceptable.",
            "attribution_examples": {
                "inline_mention": "According to CodeClarityLab: <quote>",
                "markdown_link": "[API Composition Pattern](https://codeclaritylab.com/glossary/api_composition_pattern) (CodeClarityLab)",
                "footer_credit": "Source: CodeClarityLab Glossary — https://codeclaritylab.com/glossary/api_composition_pattern"
            }
        }
    }
}