What are the three routes to one client-facing GraphQL graph over many services?
answer
- Count the routes, then count the owners
- One server can simply call them all
- Central mapping versus service-declared
- Where the join lives decides who changes it
basics
~20 sA single aggregating server whose resolvers call the backends; a gateway that stitches remote GraphQL schemas using mapping held centrally; and a composed graph, where each service declares its own contribution and a composition step merges them.
solid answer
~50 sThe three differ in **where the joining knowledge lives**, and that is the only axis that matters in practice. In an *aggregating server* there is one GraphQL schema authored in one place; its resolvers call REST, gRPC or databases, and the backends need not know GraphQL exists — the join is procedural code in that one server. In *gateway-side stitching* each service exposes its own GraphQL schema and a gateway pulls them in, but the rules saying which field on one type resolves through which query on another service are configuration owned by the gateway, so cross-service changes go through whoever runs it. In a *composed graph* each service publishes a schema that declares its own keys and ownership, and a composition step merges those declarations; the join is written by the owning team. Route three is the only one with a written composition specification behind it.
code
pseudocode · 3 lines# one aggregating server; the backends speak REST, not GraphQL
resolve Vehicle.openMaintenanceOrders(vehicle, ctx):
return ctx.maintenanceApi.ordersFor(vin = vehicle.vin, status = "OPEN")go deeper
Know that more than one arrangement exists, and that the simplest is a single GraphQL server whose resolvers call ordinary REST or gRPC backends. Being able to name that route and say the backends need not speak GraphQL is enough at this level.
Explain all three and, more importantly, the axis: where the joining knowledge is written. Be ready to say who has to make a change for a cross-service field in each, and which of the three has a specification behind it.
Show that you read these as ownership arrangements rather than technologies. Talk about how they nest, what a mixed intermediate state looks like during migration, and which route puts the change on the data owner's calendar.
Own the direction of travel. The choice determines whether cross-boundary changes queue at a central team, so decide who accumulates joining knowledge and what it costs to move that knowledge later, before any tool is selected.
## The axis that actually separates them All three routes produce the same thing for a caller — one endpoint, one schema. What differs is **who has to make a change when a client wants a field that crosses a service boundary**, and that follows directly from where the joining knowledge is written down. Keep a fleet telematics graph in mind: a vehicles service owns `Vehicle`, a maintenance service owns `MaintenanceOrder`, and someone wants `Vehicle.openMaintenanceOrders` to exist. ## Route one: a single aggregating server One GraphQL server, one schema, authored in one repository. Its resolvers are ordinary code that calls whatever the backends speak — REST, gRPC, a message-driven read model, a database. The backends do not know GraphQL exists and are not modified at all. The join for `Vehicle.openMaintenanceOrders` is a resolver: given a resolved `Vehicle`, call the maintenance service with its VIN. That is it. There is no additional specification, no composition step, no extra network hop between graph components, and no new component to operate. The cost is coordination. Every team that wants a field in the graph sends a pull request to the one repository, and every schema change ships on that server's release cadence. With one team, or three teams who talk daily, this is barely a cost. With a dozen, it is the whole problem. ## Route two: gateway-side stitching Here each service already exposes a GraphQL schema of its own. A gateway fetches those remote schemas, presents their combined type system, and delegates each incoming selection to the service that owns it. The distinguishing feature is that the *stitching* — renaming a colliding type, extending `Vehicle` with a field backed by the maintenance service, saying which argument of which remote query to call with which parent value — lives in configuration at the gateway. The services themselves are unchanged and unaware. This is a library technique, not something any specification defines, and its consequence is structural: the gateway's configuration becomes a second place where cross-service knowledge accumulates, owned by whoever runs the gateway rather than by the teams whose data it describes. ## Route three: a composed graph Each service publishes a schema annotated with its own ownership information — which types it contributes, which fields identify an object so another service can refer to the same one, which fields it borrows. A composition step takes those published schemas and produces a single graph, and a router uses the result to plan a client's operation across the services. The inversion is the point. The maintenance team adds `openMaintenanceOrders` to its own schema, in its own repository, on its own deploy. Nobody edits a central mapping file. Composition is defined by a published composition specification — the Apollo Federation specification is the widely used one — which is what makes it portable across servers in a way stitching configuration is not. The cost is a real platform: a composition step in CI, somewhere to publish and version schemas, a router to run, and a class of build failure where two services' declarations cannot be merged at all. ## What is specified and what is not Worth saying plainly, because interviewers probe it: **the GraphQL specification says nothing about combining schemas.** It defines one type system, one document language and one execution algorithm for a single schema. Route one needs no extra specification because it never combines anything. Route two is a library technique with no standard behind it. Route three is standardised, but by a separate composition specification layered on top of GraphQL, not by GraphQL itself. ## They are not mutually exclusive In real systems these nest. A service participating in a composed graph is frequently itself an aggregating server sitting over three REST APIs, and nothing about the composed graph knows or cares. Migrations run mixed for months: a stitched gateway that fronts one composed graph and two hand-written schemas is an ordinary intermediate state, not a mistake. So the useful question in an interview is never "which is correct" but "what changes when a cross-boundary field is added, and whose calendar does that land on". Route one lands on the shared server's owners. Route two lands on the gateway's owners. Route three lands on the team that owns the data — which is exactly why organisations with many contributing teams end up there, and why organisations with two teams usually should not.
- Which of the three routes requires nothing beyond the GraphQL specification itself?The aggregating server. It is an ordinary GraphQL server with one schema, and it never merges schemas at all — the backends are just data sources its resolvers call. Stitching is a library technique no specification defines, and composition is standardised by a separate composition specification layered on GraphQL, not by GraphQL itself.
- Can these routes coexist in one system?Routinely. A service participating in a composed graph is very often itself an aggregating server over several REST APIs, and the composed graph neither knows nor cares. Migrations also run mixed for months — a gateway fronting one composed graph plus two hand-written schemas is a normal intermediate state rather than a design error.
- A client wants a new field that crosses two services. Whose work is it in each route?In the aggregating server it is a change to the one shared repository, on that server's release cadence. In stitching it is a change to the gateway's mapping configuration, owned by whoever runs the gateway. In a composed graph it is a change to the owning service's own published schema, shipped on that team's cadence and picked up at the next composition.
Three ways to serve one dinner: one chef cooking every course; a head waiter who collects plates from three kitchens according to his own written plan; or three kitchens that label their plates so any host can assemble the meal.
saying these in an interview costs you the question
- Calls every multi-service GraphQL setup federation
- Treats stitching and composition as the same technique
- Thinks backing services must speak GraphQL
- Believes the GraphQL specification defines schema merging
- Says an aggregating server cannot serve many backends
- Assumes the three routes cannot coexist