# How to Implement API Aggregation and Orchestration with Easegress: A Complete Guide

> Learn how to implement API aggregation and orchestration with Easegress. Easily fan out requests to multiple backends and merge responses using declarative Pipelines.

- Repository: [MegaEase/easegress](https://github.com/megaease/easegress)
- Tags: how-to-guide
- Published: 2026-03-07

---

**Easegress implements API aggregation and orchestration by chaining RequestBuilder and ResponseBuilder filters inside a declarative Pipeline, allowing you to fan out requests to multiple backends and merge their responses using Go templates.**

Easegress is a cloud-native traffic orchestration system that simplifies complex microservice interactions through declarative configuration. This guide explains how to implement API aggregation and orchestration with Easegress using its Pipeline architecture to combine multiple backend services into unified responses.

## Understanding the Core Components for API Aggregation

### RequestBuilder Filter

The **RequestBuilder** filter, implemented in [`pkg/filters/builder/requestbuilder.go`](https://github.com/megaease/easegress/blob/main/pkg/filters/builder/requestbuilder.go), duplicates or transforms the original client request for distribution to multiple backends. When configured with `sourceNamespace: DEFAULT`, it copies the incoming request into specific namespaces (such as `demo1`, `demo2`, `demo3`) that isolate each backend call's context.

### Proxy Filter

The **Proxy** filter ([`pkg/filters/proxy/proxy.go`](https://github.com/megaease/easegress/blob/main/pkg/filters/proxy/proxy.go)) executes the actual HTTP requests against target services. Each proxy instance in the pipeline sends its namespaced request to a specific backend URL and records the response for later aggregation.

### ResponseBuilder Filter

Located in [`pkg/filters/builder/responsebuilder.go`](https://github.com/megaease/easegress/blob/main/pkg/filters/builder/responsebuilder.go), the **ResponseBuilder** assembles the final output using Go templates. It accesses upstream responses through the `{{.responses.<namespace>.Body}}` or `{{.responses.<namespace>.JSONBody}}` variables, allowing you to concatenate, merge, or transform multiple payloads into a single cohesive response.

## Building a Basic API Aggregation Pipeline

To create a simple API aggregation that calls three backends and returns their responses as a JSON array, define the following Pipeline configuration:

```yaml
name: pipeline-api
kind: Pipeline
flow:
  - filter: copyRequest
    namespace: demo1
  - filter: copyRequest
    namespace: demo2
  - filter: copyRequest
    namespace: demo3
  - filter: proxy-demo1
    namespace: demo1
  - filter: proxy-demo2
    namespace: demo2
  - filter: proxy-demo3
    namespace: demo3
  - filter: buildResponse
filters:
  - name: copyRequest
    kind: RequestBuilder
    sourceNamespace: DEFAULT
  - name: proxy-demo1
    kind: Proxy
    pools:
      - servers:
          - url: https://demo1
  - name: proxy-demo2
    kind: Proxy
    pools:
      - servers:
          - url: https://demo2
  - name: proxy-demo3
    kind: Proxy
    pools:
      - servers:
          - url: https://demo3
  - name: buildResponse
    kind: ResponseBuilder
    template: |
      statusCode: 200
      body: |
        [{{.responses.demo1.Body}}, {{.responses.demo2.Body}}, {{.responses.demo3.Body}}]

```

Expose this pipeline through an HTTPServer:

```yaml
kind: HTTPServer
name: server-demo
port: 10080
keepAlive: true
https: false
rules:
  - paths:
      - pathPrefix: /api
        backend: pipeline-api

```

Deploy the configuration using `egctl create -f pipeline-api.yaml` and `egctl create -f http-server.yaml`. Requests to `http://127.0.0.1:10080/api` return a JSON array aggregating responses from all three backend services.

## Advanced Orchestration Patterns

### Merging JSON Objects

Instead of concatenating raw bodies, use the `mergeObject` helper function to combine JSON objects from multiple backends into a unified payload:

```yaml
- name: buildResponse
  kind: ResponseBuilder
  template: |
    statusCode: 200
    body: |
      {{mergeObject .responses.demo1.JSONBody .responses.demo2.JSONBody .responses.demo3.JSONBody | toRawJson}}

```

This produces a single merged JSON object rather than an array, useful when backends return complementary data structures.

### Failure Handling and Circuit Breaking

Add resilience to your aggregation pipeline using `jumpIf` conditions to handle backend failures gracefully:

```yaml
flow:
  - filter: proxy-demo1
    namespace: demo1
    jumpIf: { serverError: proxy-demo2 }
  - filter: proxy-demo2
    namespace: demo2

```

When `demo1` returns a server error, the pipeline jumps to the next filter rather than failing entirely. You can also use `jumpIf` to skip aggregation steps or return cached responses when backends are unavailable.

### Conditional Aggregation

Leverage Go template logic within the ResponseBuilder to conditionally include backend responses based on status codes or content:

```yaml
template: |
  statusCode: 200
  body: |
    {
      {{if eq .responses.demo1.StatusCode 200}}
      "service1": {{.responses.demo1.JSONBody}},
      {{end}}
      {{if eq .responses.demo2.StatusCode 200}}
      "service2": {{.responses.demo2.JSONBody}}
      {{end}}
    }

```

This pattern creates resilient aggregations that only include successful responses, preventing partial failures from breaking the client experience.

## Key Source Files and Implementation Details

Understanding the underlying implementation helps debug complex aggregation scenarios:

| Component | File Path | Purpose |
|-----------|-----------|---------|
| **RequestBuilder** | [`pkg/filters/builder/requestbuilder.go`](https://github.com/megaease/easegress/blob/main/pkg/filters/builder/requestbuilder.go) | Implements request duplication and template-based request generation for multi-backend fan-out. |
| **ResponseBuilder** | [`pkg/filters/builder/responsebuilder.go`](https://github.com/megaease/easegress/blob/main/pkg/filters/builder/responsebuilder.go) | Provides Go template rendering with access to the `responses` map and helper functions like `mergeObject` and `toRawJson`. |
| **Pipeline Runtime** | [`pkg/object/pipeline/pipeline.go`](https://github.com/megaease/easegress/blob/main/pkg/object/pipeline/pipeline.go) | Manages the `responses` namespace map and orchestrates filter execution flow, including `jumpIf` handling. |
| **Proxy Filter** | [`pkg/filters/proxy/proxy.go`](https://github.com/megaease/easegress/blob/main/pkg/filters/proxy/proxy.go) | Executes HTTP requests against backend services and stores responses in their respective namespaces. |
| **Documentation** | [`docs/02.Tutorials/2.3.Pipeline-Explained.md`](https://github.com/megaease/easegress/blob/main/docs/02.Tutorials/2.3.Pipeline-Explained.md) | Official tutorial covering API aggregation patterns and advanced configuration options. |

## Summary

- **Easegress** enables API aggregation and orchestration through declarative Pipeline configurations using `RequestBuilder`, `Proxy`, and `ResponseBuilder` filters.
- The **RequestBuilder** duplicates client requests into isolated namespaces for parallel backend execution.
- The **ResponseBuilder** accesses upstream responses via `{{.responses.<namespace>.Body}}` templates and supports helper functions like `mergeObject` for sophisticated payload combination.
- **Failure handling** uses `jumpIf` conditions to create resilient pipelines that gracefully handle backend outages.
- All orchestration logic is configuration-driven, requiring no custom code changes to the Easegress core.

## Frequently Asked Questions

### How does Easegress handle concurrent backend requests in an aggregation pipeline?

Easegress executes filters sequentially according to the `flow` definition, but each backend call within its own namespace operates independently. The `RequestBuilder` creates isolated request contexts for each namespace, and the `Proxy` filters execute these requests. The pipeline waits for all upstream `Proxy` filters to complete before the `ResponseBuilder` executes, effectively aggregating concurrent backend responses into a single client response.

### Can I modify request headers differently for each backend service?

Yes. Configure separate `RequestBuilder` filters for each backend with distinct `template` fields that override headers, paths, or methods. For example, you can set `template: | headers: X-Service: demo1` in one RequestBuilder and `X-Service: demo2` in another. Each namespaced request carries its specific modifications when forwarded to its respective `Proxy` filter.

### What happens if one backend fails during aggregation?

By default, the pipeline continues execution unless configured otherwise. Use the `jumpIf` field in your `flow` configuration to handle specific HTTP status codes or errors. For example, `jumpIf: { serverError: nextFilter }` skips the failed backend and continues aggregation with available responses. The `ResponseBuilder` can then check `{{.responses.demo1.StatusCode}}` to conditionally include only successful responses in the final payload.

### Is there a performance limit to how many backends I can aggregate?

Easegress does not impose a hardcoded limit on the number of backends per pipeline, but each additional `Proxy` filter consumes memory and connection pool resources. According to the implementation in [`pkg/object/pipeline/pipeline.go`](https://github.com/megaease/easegress/blob/main/pkg/object/pipeline/pipeline.go), the `responses` map stores every upstream result in memory until the `ResponseBuilder` executes. For high-cardinality aggregation (tens of backends), consider memory usage and implement timeouts via the `Proxy` filter's `timeout` configuration to prevent resource exhaustion.