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

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, 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) 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, 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:

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:

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:

- 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:

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:

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 Implements request duplication and template-based request generation for multi-backend fan-out.
ResponseBuilder 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 Manages the responses namespace map and orchestrates filter execution flow, including jumpIf handling.
Proxy Filter 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 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, 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →