The client experience — one graph, no seams
Federation's promise to clients is that the supergraph looks like one coherent API. Keeping that promise is a set of deliberate practices — break them and clients start routing around your graph.
Clients must never see the topology
- No subgraph names in public types or errors.
InventoryServiceProductand errors like"subgraph inventory timed out"leak your org chart into client code. Mask and translate at the router. - Consistent conventions across subgraphs — pagination, error payloads, ID formats, naming. This is why governance linting is global, not per-team: the client experiences the union of every team's habits.
- One auth story. Tokens, scopes and error codes for authz failures must be identical whichever subgraph resolves the field.
Operate a client registry
Know your consumers: every client (web, iOS, Android, partner X) registers with a name and version on every request (apollographql-client-name style headers or equivalent).
This single habit unlocks:
- Deprecation with names attached — "checkout-web v2.1 still uses
price" beats broadcast emails. - Per-client rate/complexity budgets (security).
- Blast-radius answers during incidents — which clients touch the failing field?
Codegen and contracts
- Publish the supergraph SDL (or a filtered contract variant per audience) as a versioned artifact clients can pin for codegen.
- Partner/public audiences get a contract graph — a filtered projection excluding internal fields — not the raw supergraph with docs saying "please ignore the internal bits."
- Generated types + persisted operations means a client literally cannot ship a query the graph doesn't support — the contract is enforced at build time on both sides.
Design review with clients in the room
The most effective federation practice we've installed at clients costs nothing: schema proposals for client-facing features get reviewed with a frontend engineer present, against real screen designs. Backend-only schema design is how you get five queries per screen and a BFF creeping back in six months later — the thing your graph was supposed to replace.
The seam test
Periodically run this audit: take your three most complex product screens and write the ideal query for each as if the graph were designed only for that screen. Diff against the queries the apps actually send. Every mismatch — extra hops, client-side joins, over-fetching, "we call this twice because the shapes don't line up" — is a seam showing. That diff is your schema backlog, prioritized by real screens.