Three paths to a distributed GraphQL architecture, and when each one fits
Once a GraphQL schema is developed by several teams in parallel, a single monolithic API hits its limits. Apollo Federation, GraphQL Mesh and Cosmo solve that problem in fundamentally different ways: through subgraph composition, through a gateway in front of arbitrary existing systems, or through an open router implementation without vendor lock-in.
Table of Contents
- 1. Why a single GraphQL schema eventually stops being enough
- 2. Apollo Federation: subgraph composition with @key and @external
- 3. Apollo Federation in practice: the router, entity resolution and query planning
- 4. GraphQL Mesh: a GraphQL gateway in front of arbitrary APIs
- 5. When GraphQL Mesh is the better choice over Federation
- 6. Cosmo: the open-source alternative to Apollo GraphOS
- 7. Cosmo vs. Apollo GraphOS: router performance and licensing
- 8. Team structure and governance: which approach fits which organization
- 9. Decision criteria: budget, team size and existing system landscape
- 10. Summary
- 11. FAQ
1. Why a single GraphQL schema eventually stops being enough
A monolithic GraphQL schema works great as long as a single team owns types, resolvers and deployments. Once several domain teams work on product, order and user data at the same time, that same codebase becomes a bottleneck: every change to a type requires coordination with every other team, deployments block each other, and a single broken resolver can take down the whole API even if it only serves a minor feature.
A distributed GraphQL architecture splits that responsibility along business boundaries: each team runs its own service with its own deployment cycle, and a central entry point composes the partial schemas into a single supergraph that stays invisible to clients. Apollo Federation, GraphQL Mesh and Cosmo mainly differ in how that entry point is built and what kind of backend it requires.
2. Apollo Federation: subgraph composition with @key and @external
Apollo Federation requires every backend service to expose its own native GraphQL subgraph schema and mark, via the @key directive, which field uniquely identifies an entity. At startup, or through Managed Federation, the central router loads a composed supergraph plan and thereby knows which subgraph contributes which field of a type, without a client ever needing to know how many services actually compose a response.
When one subgraph references a type owned by another service, the @external directive marks the field as defined elsewhere, while @requires and @provides control which extra data is needed for entity resolution. This directive system is powerful, but it demands a clear understanding of entity ownership from every team, otherwise circular dependencies between subgraphs appear that only surface as errors at runtime.
# Subgraph: products
type Product @key(fields: "id") {
id: ID!
name: String!
price: Float!
weightInGrams: Int! @external
shippingEstimate: String @requires(fields: "weightInGrams")
}
# Subgraph: reviews
type Review {
id: ID!
rating: Int!
product: Product!
}
extend type Product @key(fields: "id") {
id: ID! @external
reviews: [Review!]!
}
3. Apollo Federation in practice: the router, entity resolution and query planning
When a client query touches several subgraphs, the Apollo Router splits it into a query plan of several parallel or sequential sub-requests to the affected services and stitches the results back together using the @key fields. Under Managed Federation, that plan is validated at build time, so incompatible schema changes show up in the CI pipeline before they ever reach production.
In practice this means the router itself runs no business logic of its own, it only orchestrates requests to the subgraphs, which keeps it lean and horizontally scalable. The price is organizational: every new subgraph team has to follow the Federation specification, which means extra adaptation work for existing REST or SOAP services that have no native GraphQL schema.
# router.yaml
supergraph:
listen: 0.0.0.0:4000
path: /graphql
telemetry:
metrics:
prometheus:
enabled: true
listen: 0.0.0.0:9090
include_subgraph_errors:
all: true
cors:
origins:
- https://mironsoft.de
4. GraphQL Mesh: a GraphQL gateway in front of arbitrary APIs
GraphQL Mesh takes a different approach: instead of requiring native GraphQL subgraphs, it automatically generates a GraphQL schema from existing REST, gRPC, SOAP or even database interfaces and serves it behind a single gateway. That makes Mesh especially suitable for organizations with a grown system landscape, where rewriting every backend to native GraphQL afterwards would not be economically justified.
Each source is wired in through a handler and optional transformers: a handler, for example, automatically translates an OpenAPI specification into GraphQL types, while transformers rename fields, flatten nested responses or define caching rules per source. This declarative configuration model saves hand-written resolvers for standard cases, but it requires careful upkeep of the Mesh configuration whenever one of the underlying APIs changes.
# .meshrc.yaml
sources:
- name: CrmApi
handler:
openapi:
source: https://crm.internal/openapi.json
baseUrl: https://crm.internal/api
- name: LegacyOrders
handler:
grpc:
endpoint: legacy-orders.internal:50051
protoFilePath: ./protos/orders.proto
transforms:
- rename:
renames:
- from: { type: CrmApi_Customer }
to: { type: Customer }
5. When GraphQL Mesh is the better choice over Federation
GraphQL Mesh shows its strength when an organization wants to unify several independent third-party systems, such as a CRM, a payment provider and an internal legacy REST system, under a single GraphQL surface without access to their source code. Because Mesh builds directly on top of each source's public interface, no team needs to maintain its own Federation directives.
The downside shows up with very complex entity relationships between sources: while Apollo Federation ships entity resolution as a core feature, that behavior often has to be rebuilt manually in GraphQL Mesh through additional resolvers or transformers, which quickly becomes hard to manage for deeply nested data models. Mesh therefore fits additive integration better than tightly coupled, jointly owned domain models.
6. Cosmo: the open-source alternative to Apollo GraphOS
Cosmo, built by WunderGraph, implements the same Federation specification as Apollo, meaning the same @key and @external directives, but replaces Apollo's proprietary Managed Federation service with a fully open-source router implementation written in Go, plus its own schema registry backend. Existing Apollo subgraphs can therefore often connect to a Cosmo router without any code change, because compatibility is guaranteed at the specification level, not the vendor level.
The core difference lies in the operating model: while Apollo GraphOS runs as a hosted service with usage-based licensing, Cosmo can be fully self-hosted, including registry, analytics and router. That makes Cosmo attractive for teams with strict data residency requirements or for organizations that want to avoid growing license costs as traffic volume increases.
# Install the Cosmo CLI
npm install -g wgc
# Publish a subgraph to the Cosmo registry
wgc subgraph publish products \
--schema ./products/schema.graphql \
--routing-url https://products.internal/graphql
# Compose the supergraph and check for breaking changes
wgc federated-graph compose mironsoft-graph
wgc subgraph check products --schema ./products/schema.graphql
7. Cosmo vs. Apollo GraphOS: router performance and licensing
The Cosmo router is written in Go and tuned for high throughput at low memory footprint, while the Apollo Router is implemented in Rust and, in independent benchmarks, has a slight edge under very high request load. For most mid-sized deployments the practical difference amounts to a few milliseconds per request, so the decision rarely comes down to raw performance alone.
Licensing usually matters more: Apollo GraphOS bills by requests and operations, which can lead to noticeable monthly costs under heavy traffic, while Cosmo, as a self-hosted open-source solution, only incurs infrastructure costs, but requires the team's own operational know-how for registry, metrics and alerting, which Apollo GraphOS delivers as a managed service.
# cosmo-router-config.yaml
version: "1"
graph:
token: "${COSMO_GRAPH_API_TOKEN}"
telemetry:
metrics:
otlp:
enabled: true
endpoint: http://otel-collector:4318
traffic_shaping:
router:
max_request_body_size: 5MB
8. Team structure and governance: which approach fits which organization
Apollo Federation and Cosmo implicitly assume that every domain team takes ownership of its own subgraph schema, including schema reviews, breaking-change detection and versioning. That fits organizations already structured around independent domain teams with clear bounded contexts, in the spirit of domain-driven design, because the technical boundary of the subgraphs lines up directly with the business boundary of the teams.
GraphQL Mesh, by contrast, fits a centralized API-platform organization better, where a single team maintains the GraphQL facade over external, often not self-controlled systems. Here the governance burden sits with exactly that one team, while the source systems themselves can keep evolving independently under their respective owners, without ever having to think about Federation concepts.
9. Decision criteria: budget, team size and existing system landscape
Anyone already running several teams with their own native GraphQL services, and who values a hosted, fully managed operation with schema registry, metrics and change checks, is well served by Apollo Federation through Apollo GraphOS, but should factor the ongoing license costs into the budget. Anyone who wants the same specification while self-hosting and avoiding license costs finds a compatible, open-source alternative in Cosmo.
Anyone who primarily wants to bundle existing REST, gRPC or legacy systems under a shared GraphQL surface, without touching their source code, gets there faster with GraphQL Mesh than with a later Federation migration. The table below summarizes the key differences between the three approaches.
| Approach | Requirement | Operating model | Best suited for |
|---|---|---|---|
| Apollo Federation | Native GraphQL subgraphs per team | Hosted (GraphOS) or self-hosted router | Domain teams with their own GraphQL schema |
| GraphQL Mesh | Any REST/gRPC/SOAP sources | Self-hosted gateway | Integrating existing third-party systems |
| Cosmo | Native GraphQL subgraphs, Federation compatible | Fully self-hosted, open source | Teams with data residency or cost constraints |
| Classic API gateway (REST) | Any sources without GraphQL | Usually self-hosted | No unified type system desired |
Mironsoft
GraphQL schema design, resolver performance and API security
GraphQL APIs that hold up under real load?
We review existing GraphQL schemas and resolvers, uncover N+1 problems and missing query limits, and turn that into an API that holds performance, security and maintainability together.
Schema Review
Checking types, resolvers and permissions for consistency and security gaps.
Performance Optimization
Deploying DataLoader, caching and query complexity limits against N+1 and overfetching.
Production Hardening
Setting up rate limiting, introspection protection and monitoring for production.
10. Summary
Apollo Federation, GraphQL Mesh and Cosmo: The Essentials at a Glance
Apollo Federation
Native GraphQL subgraphs, @key/@external directives, hosted via GraphOS or a self-hosted router.
GraphQL Mesh
A gateway in front of arbitrary REST/gRPC/SOAP sources, ideal for integrating existing third-party systems without code changes.
Cosmo
Open-source router by WunderGraph, Federation compatible, fully self-hosted without license costs.
Decision
Team structure and data residency usually matter more than raw performance differences between the routers.