Federation Gateway
When to Use the Built-in Gateway
Section titled “When to Use the Built-in Gateway”The fraiseql federation gateway command federates multiple FraiseQL subgraphs into a single endpoint without Apollo Router.
| Built-in Gateway | Apollo Router | |
|---|---|---|
| Best for | FraiseQL-only subgraphs | Mixed subgraphs (FraiseQL + non-FraiseQL) |
| Dependencies | None — single fraiseql binary | Separate binary + Rover CLI |
| Query planning | Type-level routing (HashMap) | Field-level routing (query planner) |
@shareable fields | Not supported | Supported |
@requires / @provides | Not supported | Supported |
| Subscriptions | Not forwarded (use direct subgraph WebSocket) | Supported |
Use the built-in gateway when:
- All subgraphs are FraiseQL services
- Each type is fully owned by one subgraph (no
@shareablefields) - You want zero external dependencies
Use Apollo Router when:
- You have non-FraiseQL subgraphs (Node.js, Java, etc.)
- You need field-level splitting or
@requires/@provides
Architecture
Section titled “Architecture”FraiseQL’s CQRS architecture makes query planning simple: each type is a single JSONB view owned by exactly one subgraph. The gateway routes at the type level (a HashMap lookup), not the field level. This eliminates the complexity of general-purpose query planners.
Quick Start
Section titled “Quick Start”- Start two FraiseQL subgraphs with federation enabled:
# Terminal 1: User servicecd user-service && fraiseql run --port 4001
# Terminal 2: Order servicecd order-service && fraiseql run --port 4002- Create
gateway.toml:
[gateway]port = 4000bind = "0.0.0.0"
[[gateway.subgraphs]]name = "users"url = "http://localhost:4001/graphql"
[[gateway.subgraphs]]name = "orders"url = "http://localhost:4002/graphql"- Start the gateway:
fraiseql federation gateway gateway.toml- Query the gateway:
curl http://localhost:4000/graphql \ -H "Content-Type: application/json" \ -d '{"query":"{ users(limit: 5) { id name } }"}'Configuration Reference (gateway.toml)
Section titled “Configuration Reference (gateway.toml)”The gateway uses a separate config file from fraiseql.toml.
[gateway]
Section titled “[gateway]”| Field | Type | Default | Description |
|---|---|---|---|
port | integer | 4000 | Gateway listen port |
bind | string | "0.0.0.0" | Bind address |
[[gateway.subgraphs]]
Section titled “[[gateway.subgraphs]]”| Field | Type | Default | Description |
|---|---|---|---|
name | string | required | Subgraph identifier |
url | string | required | Subgraph GraphQL endpoint URL |
[gateway.circuit_breaker]
Section titled “[gateway.circuit_breaker]”| Field | Type | Default | Description |
|---|---|---|---|
failure_threshold | integer | 5 | Failures before circuit opens |
recovery_timeout_secs | integer | 30 | Seconds before half-open retry |
[gateway.cache]
Section titled “[gateway.cache]”| Field | Type | Default | Description |
|---|---|---|---|
sdl_ttl_secs | integer | 300 | Cache subgraph SDL for this duration |
query_plan_cache_size | integer | 1000 | Max cached query plans |
How Query Routing Works
Section titled “How Query Routing Works”- Parse incoming GraphQL query
- Look up owning subgraph for each root type (type → subgraph map built from SDL introspection at startup)
- Fan out per-subgraph queries in parallel
- Collect responses
- Scan for
@keyUUID references to other subgraphs - Batch
_entitiescalls to resolve cross-subgraph references - Stitch into final response
The type → subgraph map is a HashMap<TypeName, SubgraphUrl>. For FraiseQL services where each type is owned by exactly one subgraph, this is all the query planner needs.
Cross-Subgraph References
Section titled “Cross-Subgraph References”When a type in one subgraph references an entity in another (e.g., Order.user_id → User.id), the gateway uses _entities batching:
# Client sends:query { orders(limit: 10) { id total user { id name email } }}
# Gateway:# 1. Fetch orders from order-service → gets user_id UUIDs# 2. Batch _entities call to user-service:# query { _entities(representations: [{__typename: "User", id: "..."}]) { id name email } }# 3. Stitch user objects into order responsesThe gateway reuses HttpEntityResolver for entity resolution — the same component FraiseQL subgraphs use internally, with SSRF protection, retry, circuit breaker, and request deduplication.
Distributed Tracing
Section titled “Distributed Tracing”The gateway propagates W3C TraceContext headers (traceparent, tracestate) to all subgraph requests. Gateway spans appear as parent spans in your tracing backend (Jaeger, Grafana Tempo, etc.).
Configure tracing on the gateway via environment variables:
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317 \OTEL_SERVICE_NAME=fraiseql-gateway \fraiseql federation gateway gateway.tomlLimitations
Section titled “Limitations”- No field-level splitting:
@shareablefields across subgraphs are not supported. Each type must be fully owned by one subgraph. - No
@requires/@provides: Pre-fetching fields from another subgraph before resolving a local field is not implemented. - No subscription forwarding: Use direct WebSocket connections to individual subgraph URLs for subscriptions.
- FraiseQL-only: Mixed subgraphs (FraiseQL + non-FraiseQL) require Apollo Router.
Migration from Apollo Router
Section titled “Migration from Apollo Router”If you currently use Apollo Router with FraiseQL-only subgraphs:
federation_version: =2.4.0subgraphs: users: routing_url: http://user-service:4001/graphql schema: subgraph_url: http://user-service:4001/graphql orders: routing_url: http://order-service:4002/graphql schema: subgraph_url: http://order-service:4002/graphql[gateway]port = 4000
[[gateway.subgraphs]]name = "users"url = "http://user-service:4001/graphql"
[[gateway.subgraphs]]name = "orders"url = "http://order-service:4002/graphql"What changes:
- Replace
supergraph.yaml+router.yamlwith a singlegateway.toml - Replace
rover supergraph compose+routercommands withfraiseql federation gateway - No
supergraph.graphqlartifact needed — the gateway introspects subgraphs at startup
What stays the same:
- Per-service
fraiseql.tomland schemas — no changes needed - JWT forwarding — the gateway passes
Authorizationheaders to all subgraphs - Circuit breaker behavior