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, andResponseBuilderfilters. - 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 likemergeObjectfor sophisticated payload combination. - Failure handling uses
jumpIfconditions 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →