A stateless, high-performance GraphQL Federation v2 Gateway written purely in Go.
Go GraphQL Federation Gateway is a lightweight, stateless GraphQL Federation v2 Gateway written purely in Go.
While existing solutions like Apollo Router (Rust) are excellent, extending them often requires learning Rust or dealing with binary constraints. This gateway provides:
- Native Go — Easy to read, debug, and extend for Go developers
- Stateless design — HTTP-only (query & mutation); no WebSocket state required
- Federation v2 Compliant — Core and advanced directives fully supported
- Hackable — Modular Planner/Executor architecture for custom optimization
- Observable — Built-in OpenTelemetry tracing support
- Faster than Apollo Router — 1.58x higher throughput in benchmarks
@keynested field support:@key(fields: "coordinate { lat lng }")— nested object keys are fully parsed and resolved via aKeyFieldNodetree structure- Multiple
@keydirectives (executor fix): When an extension subgraph declares a different@keythan the owner's first key, the executor now correctly uses the extension's key for_entitiesrepresentations (e.g.,@key(fields: "username")in badges service when owner also has@key(fields: "id")) @providesoptimization: Entity steps are skipped when the parent service fully covers all requested child fields via@provides. Safety check ensures the optimization only applies when the entity type is declared as a fulltypedefinition (notextend type)
- HTTP connection pool settings via
gateway.yaml:max_idle_conns_per_host(default 32),max_conns_per_host, andidle_conn_timeoutare now configurable — eliminating the previous bottleneck ofMaxIdleConnsPerHost: 2fromhttp.DefaultTransport sync.Poolrequest body buffering:sendRequestnow uses a pool of*bytes.Bufferinstances, eliminating per-requestjson.Marshalintermediate[]byteallocation andbytes.NewReaderwrapper- Result: 1.58x faster than Apollo Router (4,380 req/s vs 2,771 req/s at concurrency 50)
- 135+ integration tests across 8 production-like domains (EC, Fintech, SaaS, Social, Travel, nestkey, multikey, provides)
- 3 new test domains demonstrating nested keys (
nestkey), alternate key resolution (multikey), and@providesoptimization (provides)
This gateway is designed to be stateless and horizontally scalable:
- HTTP-only: query and mutation operations
- No persistent WebSocket connections (Subscription is intentionally out of scope)
- Every request is fully self-contained
- DAG-based execution: Resolves complex dependency graphs, executing independent steps in parallel
@requiresinjection: Automatically injects required fields (e.g.,weight) into upstream requests for computed fields (e.g.,shippingCost). Supports chained@requiresdependencies@providesoptimization: Skips unnecessary_entitiesfetches when the parent service already provides the needed fields@shareablepreference: Prefers the parent's subgraph for@shareablefields to avoid extra entity fetches- Entity step deduplication: Sibling extension fields on the same entity are merged into a single
_entitiescall - Query plan caching: Plans are cached per query string and reused on cache hits, skipping parse/validate/plan on repeated queries
- Independent subgraph steps execute in parallel via goroutines
- Step dependency graph (DAG) ensures correct ordering
- Returns partial data when some subgraphs fail
- Failed fields are set to
nullwith error details including path and service name - Execution continues for independent fields even when some steps fail
Configurable via gateway.yaml:
connection_pool:
max_idle_conns_per_host: 32 # default: 32
max_conns_per_host: 0 # default: 0 (unlimited)
idle_conn_timeout: "90s" # default: "90s"| Directive | Status | Notes |
|---|---|---|
@key |
✅ | Simple, composite, nested ("coord { lat lng }"), multiple @key per type |
@key(resolvable: false) |
✅ | Excluded from entity fetch origin selection |
@external |
✅ | Correct ownership filtering, prevents stub references from being treated as owners |
@requires |
✅ | Chained dependency injection in topological order |
@provides |
✅ | Optimization: skips entity step when parent covers all requested fields |
@shareable |
✅ | Same-subgraph preference to avoid unnecessary entity fetches |
@override |
✅ | Field ownership migration between subgraphs |
@inaccessible |
✅ | Type & field level; enforced at schema composition and query validation |
@tag |
✅ | Type & field level metadata |
@interfaceObject |
✅ | Interface types as entities; inline fragment resolution |
@composeDirective |
✅ | Custom directive preservation and consistency validation |
@link |
✅ | Parsed and passed through in composed schema |
| Feature | Reason |
|---|---|
| Subscription | Requires stateful WebSocket connections; contradicts stateless design |
@authenticated / @requiresScopes / @policy |
Authorization layer should be handled outside the gateway |
@defer / @stream |
Requires long-lived streaming connections |
| Federated Tracing (ftv1) | Apollo Studio-specific protocol |
@override(label) |
Progressive override; planned for future release |
The planner relies on explicit schema definitions to determine field ownership.
Always use extend type and mark external fields with @external:
# ✅ Recommended
extend type Product @key(fields: "id") {
id: ID! @external
weight: Float @external
shippingCost: Float @requires(fields: "weight")
}go install github.com/n9te9/go-graphql-federation-gateway/cmd/go-graphql-federation-gateway@latestInitialize configuration:
go-graphql-federation-gateway initThis generates a gateway.yaml with sensible defaults:
endpoint: /graphql
port: 9000
service_name: go-graphql-federation-gateway
timeout_duration: "5s"
request_timeout: "30s"
services:
- name: products
host: http://localhost:4001
retry:
attempts: 3
timeout: "5s"
connection_pool:
max_idle_conns_per_host: 32
max_conns_per_host: 0
idle_conn_timeout: "90s"
opentelemetry:
tracing:
enable: falseStart the gateway:
go-graphql-federation-gateway servegit clone https://github.com/n9te9/go-graphql-federation-gateway.git
cd _example/ec
docker compose up -dThen start the gateway:
cd _example/ec
go-graphql-federation-gateway servecd _example
make test-all # All 8 domains (135+ tests)
make test-ec # EC domain only
make test-nestkey # Nested @key tests
make test-multikey # Multiple @key directive tests
make test-provides # @provides optimization testsBenchmarks run at concurrency 50 with 10,000 requests per domain (Docker internal networking):
| Domain | Go Gateway | Apollo Router | Ratio |
|---|---|---|---|
EC — @external + @requires |
~4,380 req/s | ~2,771 req/s | 1.58x faster |
Run benchmarks locally:
cd _example
make setup # Install hey, verify Docker
make benchmark # All domainsEnable OpenTelemetry tracing in gateway.yaml:
opentelemetry:
tracing:
enable: trueConfigure the exporter via environment variable:
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"- Fork the project
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
Distributed under the MIT License. See LICENSE for more information.