Api Composition Patterns

Production engineering guide for api composition patterns covering patterns, implementation strategies, and operational best practices.

Api composition patterns are the structural strategies for combining multiple APIs into a cohesive, unified service layer. They matter when a system must orchestrate data and behavior across independently developed services—typically in a microservices or event-driven architecture—where a single client request requires coordination of two or more backend APIs. Composition is essential when a client needs a view or action that cannot be satisfied by any single API alone, and when latency, consistency, and resilience across the composed stack become critical.

Request-Driven Composition

Compose APIs in response to a client request, where the result is a single, unified payload from multiple underlying services. This is the most common pattern, used when clients expect a rich, denormalized response without needing to manually stitch together results.

Sequential Chaining

A client request triggers a series of synchronous API calls, where the output of one API becomes the input of the next.

GET /users/123
  1. Fetch user profile
   GET /api/v1/users/123
   {
     "id": 123,
     "name": "Alice",
     "email": "[email protected]",
     "departmentId": 45
   }
  1. Fetch department details
   GET /api/v1/departments/45
   {
     "id": 45,
     "name": "Engineering",
     "managerId": 567,
     "budget": 1200000
   }
  1. Fetch manager details
   GET /api/v1/users/567
   {
     "id": 567,
     "name": "Bob",
     "email": "[email protected]",
     "title": "Director"
   }

Final response:

{
  "user": {
    "id": 123,
    "name": "Alice",
    "email": "[email protected]"
  },
  "department": {
    "name": "Engineering",
    "manager": {
      "name": "Bob",
      "email": "[email protected]",
      "title": "Director"
    },
    "budget": 1200000
  }
}

Failure Modes

Configuration

Parallel Composition

Compose multiple APIs concurrently, then merge their results. Best when dependencies between services are independent.

GET /orders/456
  1. Parallel calls:
  1. Merge results:
{
  "order": {
    "id": 456,
    "customerId": 789,
    "total": 245.99,
    "status": "shipped"
  },
  "customer": {
    "name": "Charlie",
    "email": "[email protected]"
  },
  "products": [
    { "id": 101, "name": "Laptop", "price": 1200 },
    { "id": 102, "name": "Mouse", "price": 25 },
    { "id": 103, "name": "Keyboard", "price": 75 }
  ],
  "shipping": {
    "trackingNumber": "TRK123456",
    "estimatedDelivery": "2025-04-10"
  }
}

Key Considerations

Configuration Example

composition:
  parallel:
    - service: "orders"
      method: "GET"
      path: "/api/v1/orders/{orderId}"
      params: { orderId: "id" }
      timeout: 1500
      retry:
        maxRetries: 2
        delay: 100
        backoff: "exponential"
    - service: "customers"
      method: "GET"
      path: "/api/v1/customers/{customerId}"
      params: { customerId: "order.customerId" }
      continueOnFailure: true
    - service: "products"
      method: "GET"
      path: "/api/v1/products"
      query: { ids: "order.productIds" }
      mergeStrategy: "deep"

Event-Driven Composition

Compose APIs in response to an event, where one or more services emit events that trigger downstream composition.

Event Sourcing Composition

A domain event triggers a composition that aggregates data from multiple services into a view model.

Example: UserCreated event

{
  "eventId": "evt-123",
  "eventType": "UserCreated",
  "timestamp": "2025-03-15T10:30:00Z",
  "data": {
    "userId": 123,
    "name": "Diana",
    "email": "[email protected]",
    "departmentId": 45
  }
}

This event is consumed by a composition service that:

  1. Fetches user from users API
  2. Fetches department from departments API
  3. Fetches team members from teams API
  4. Aggregates into a UserProfileView stored in a read model.

Configuration

eventHandlers:
  UserCreated:
    - action: "fetch-user"
      service: "users"
      method: "GET"
      path: "/api/v1/users/{userId}"
      target: "user"
    - action: "fetch-department"
      service: "departments"
      method: "GET"
      path: "/api/v1/departments/{departmentId}"
      target: "department"
    - action: "fetch-team"
      service: "teams"
      method: "GET"
      path: "/api/v1/teams/{departmentId}/members"
      target: "team"
    - action: "merge-views"
      strategy: "merge"
      output: "UserProfileView"
      schema: "/schemas/user-profile.json"

Failure Modes

Event-Driven Orchestration

Multiple events trigger a sequence of composition steps, where each step may involve multiple API calls and emit new events.

Example: OrderPlaced → OrderFulfilled

  1. OrderPlaced event published.
  2. Composition service:
  1. On success, emits OrderFulfilled event.
  2. On failure, emits OrderFailed event.

Each API call may be configured with:

{
  "service": "inventory",
  "method": "POST",
  "path": "/api/v1/reserve",
  "input": {
    "orderId": "event.orderId",
    "items": "event.items"
  },
  "timeout": 1000,
  "retry": {
    "maxAttempts": 3,
    "backoff": "exponential",
    "delay": 100
  },
  "errorHandling": {
    "strategy": "compensate",
    "compensatingAction": "refund-order"
  }
}

Key Design Decisions

Schema-Driven Composition

Compose APIs based on a declarative schema that defines the input, output, and transformation logic.

Schema Definition

{
  "name": "UserWithDepartment",
  "input": {
    "type": "object",
    "properties": {
      "userId": { "type": "integer" }
    },
    "required": ["userId"]
  },
  "output": {
    "type": "object",
    "properties": {
      "user": { "$ref": "#/definitions/User" },
      "department": { "$ref": "#/definitions/Department" },
      "manager": { "$ref": "#/definitions/User" }
    }
  },
  "steps": [
    {
      "name": "fetch-user",
      "service": "users",
      "method": "GET",
      "path": "/api/v1/users/{userId}",
      "output": "user"
    },
    {
      "name": "fetch-department",
      "service": "departments",
      "method": "GET",
      "path": "/api/v1/departments/{user.departmentId}",
      "output": "department"
    },
    {
      "name": "fetch-manager",
      "service": "users",
      "method": "GET",
      "path": "/api/v1/users/{department.managerId}",
      "output": "manager"
    }
  ],
  "transform": {
    "merge": {
      "target": "output",
      "source": [ "user", "department", "manager" ]
    }
  }
}

Usage

POST /api/v1/compose/UserWithDepartment
{
  "userId": 123
}

Response:

{
  "user": { "id": 123, "name": "Alice", "email": "[email protected]" },
  "department": { "id": 45, "name": "Engineering" },
  "manager": { "id": 567, "name": "Bob" }
}

Deployment and Discovery

Schema Versioning

When the schema changes, the composition system must handle versioning:

Each version may have different steps, transformation rules, or error handling.

Composition Layer Failure Patterns

Composition Strategies Summary

StrategyBest ForLatencyFault ToleranceIdempotency
Sequential ChainingDependent, linear workflowsHighModerateHigh
Parallel CompositionIndependent, high-throughput APIsLowHighMedium
Event-DrivenReal-time, asynchronous, reactive systemsMediumHighHigh
Schema-DrivenReusable, declarative, versioned APIsMediumHighHigh

Each pattern solves a distinct problem in API composition. Choose based on whether the request is driven by a client, an event, or a schema—and how resilient and performant the composition must be.

This page was rewritten on 17 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.