skip to content

questions

4

In Apollo Federation 2, why can only one subgraph resolve a field unless it is @shareable?

level: middleimportance: must knowfreq 58%

answer

  1. The router must know where to fetch
  2. One declaration per field, by default
  3. Every participating subgraph must say it
  4. Key fields are already resolvable everywhere
  5. A promise nothing checks at runtime

basics

~20 s

Federation 2 assumes one owner per field so the router always knows where to fetch it and the answer never depends on the query plan. @shareable is the explicit opt-out: several subgraphs may resolve the field, and they promise to agree.

solid answer

~40 s

Composition has to name one subgraph for every field in the supergraph. With a single owner that mapping is unambiguous; with two owners and no declaration the value a client sees would depend on which subgraph the query plan happened to visit, so composition refuses the pair. `@shareable` declares the sharing is intended — and it is a claim each subgraph makes about itself, so every subgraph resolving the field must carry it, not just one. Entity key fields are exempt, because any subgraph referencing the entity must be able to return its key. The catch is that `@shareable` is a promise nothing verifies: the router picks whichever implementation its plan already reaches, so divergent implementations surface as a value that changes with the shape of the query.

code

graphql · 13 lines
graphql
# Lockers subgraph
type Locker @key(fields: "id") {
  id: ID!
  status: LockerStatus! @shareable
  address: String!
}

# Reservations subgraph — omitting @shareable here fails composition
type Locker @key(fields: "id") {
  id: ID!
  status: LockerStatus! @shareable
  activeHolds: Int!
}

go deeper

for a junior

Remember the default: a field is resolved by one subgraph. If you need to add the same field in a second subgraph, that is a decision to raise, not a line to copy.

for a middle

Explain why composition needs one owner per field, that the marker must appear in every subgraph declaring the field, and why key fields are exempt.

for a senior

Show that you know nothing enforces the promise: be ready to describe how divergent implementations surface as plan-dependent values or plan-dependent latency, and how you would find that.

for a principal

Own the policy. Decide when a field may have two homes at all, who arbitrates the definition when teams disagree, and how a schema check or lint keeps sharing from spreading through shared value types unnoticed.

## Ownership is the default, sharing is the exception Composition turns several subgraph schemas into one supergraph schema, and the router uses that supergraph to build a query plan. For every field the plan touches, the router must answer one question: which subgraph do I call for this? The default answer in Federation 2 is "the single subgraph that declares it". If two subgraphs declare `Locker.address` and neither says anything more, composition fails rather than picking a winner — because a graph where the answer depends on the plan is a graph where the value depends on the plan, and query plans change as the schema and the client's selection change. `@shareable` changes that answer. Applied to a field, it declares that this field may be resolved in more than one subgraph. Applied to an object type, it applies to every field of that type. ```graphql # Lockers subgraph type Locker @key(fields: "id") { id: ID! status: LockerStatus! @shareable } # Reservations subgraph type Locker @key(fields: "id") { id: ID! status: LockerStatus! @shareable activeHolds: Int! } ``` ## It is a claim, not a permission The rule candidates most often get wrong: `@shareable` is not something the owner grants to the others. It is a statement each subgraph makes about its own declaration, so **every** subgraph that resolves the field must carry it. Mark it in Lockers and forget Reservations, and composition still rejects the pair, because Reservations is still asserting sole ownership of `status`. Two categories need no marker at all. Entity key fields are implicitly resolvable everywhere: any subgraph that references `Locker` has to be able to return the fields inside its `@key` so the router can build a representation, so the duplicate-declaration rule does not apply to them. And a field marked `@external` is not a second implementation — it is a reference to a field owned elsewhere, declared so this subgraph can mention it in a key or in another ownership directive. ## Value types are where this shows up first The common encounter is not entities but *value types*: plain object types with no `@key` — `Address`, `Money`, `TimeWindow` — that several teams want in their own schemas. Federation 1 merged identical value-type definitions silently. Federation 2 reversed the default: each shared field must be marked, or the whole type marked. That is more typing, and it is deliberate. "Several teams own this shape" becomes a decision someone wrote down rather than a merge that happened quietly. ## The part nothing enforces `@shareable` is a promise about behaviour, and neither composition nor the router checks it. The router is free to satisfy the field from whichever subgraph its plan already needs to visit, and it will generally pick the one that saves a fetch. Take a parcel-locker graph where `Locker.status` is shareable. The Lockers subgraph derives it from the device's last heartbeat. The Reservations subgraph derives it from booking state, because it already has the booking row loaded. Now: - `query { locker(id: "L-4471") { status } }` starts in Lockers and returns the heartbeat view. - `query { reservation(id: "R-88213") { locker { status } } }` is already inside Reservations, which can answer without a second fetch — so it returns the booking-derived value, and Lockers is never called. Same field, same client, two answers, and no error anywhere. Worse, the behaviour moves under you: a change to an unrelated part of the query, or to the planner, can flip which implementation answers. This is why the complete interview answer is "it lets several subgraphs resolve the field **and obliges them to agree**", not just the first half. There is a latency face to the same trap. If Reservations computes `status` from a row it already loaded but Lockers calls a device registry, then a plan that routes through Lockers adds a hop the other path does not have. A field that is free on one route and 90 ms on another makes a 340 ms p99 budget a lottery decided by query shape, and the p99 regression will look like it came from nowhere because no subgraph got slower. ## When not to reach for it Prefer a single owner and let other subgraphs reference the entity by key. Reserve sharing for fields that are cheap, stable and genuinely denormalised in more than one store — a display name, a formatted amount, a status that is a true copy. Anything with business logic behind it should have one home. If two teams disagree about what a field *means*, `@shareable` will not settle the argument; it will hide it behind a query plan.

  • Why do entity key fields not need @shareable even though several subgraphs declare them?
    Because every subgraph that references the entity must be able to return its key — that is how the router addresses an instance across services. Repeating the key is addressing, not a competing implementation, so the duplicate rule does not apply. The same logic covers fields marked `@external`: they name a field owned elsewhere rather than offering a second one.
  • A shareable field returns different values depending on the query shape. How would you diagnose it?
    Reproduce both selections and compare the query plans — the difference will be which subgraph the plan reaches the field from. Then compare the two implementations, since composition never did. The fix is usually to remove the sharing and leave one owner, not to make the router prefer a subgraph, because that preference is not something you can rely on.
  • Is marking a whole type @shareable the same as marking every field?
    For the fields present when you write it, yes — the type-level form applies to each of its fields. The difference is what happens later: a field added to that type afterwards is shared automatically, without anyone deciding. On a type several teams touch, marking fields individually keeps each sharing decision explicit.

Two branches of a shop may both quote a price only if head office says they share the price list — and the customer will believe whichever branch they walked into.

saying these in an interview costs you the question

  • Thinks one subgraph marks it and the rest inherit
  • Says composition verifies the two implementations agree
  • Believes the router always prefers the owning subgraph
  • Marks key fields @shareable thinking it is required
  • Treats it as a performance feature rather than an ownership statement

context

open as a page

How does @override(from:) move field ownership between subgraphs safely?

level: seniorimportance: should knowfreq 40%

basics

~20 s

The receiving subgraph declares the field with @override(from:) naming the subgraph it is taking over from; composition then routes every request for that field to the new one. Deploy the new implementation first, compose, verify, then delete the old field.

open as a page

When does @provides in a federated subgraph actually save the router a fetch?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Only when the client reaches the entity through the exact field carrying the directive, and only if the resolver really returns the promised values. Reached by any other path, the router still fetches the field from the subgraph that owns it.

open as a page

In a Federation 1 subgraph schema, what does the extend keyword on a type mean?

level: juniorimportance: nice to knowfreq 24%

basics

~20 s

The extend keyword marks the type as owned by another subgraph: this schema only contributes extra fields to a type it did not define, so composition treats the two declarations as one type rather than a clash.

open as a page