Skip to content

[Fusion] Fix node lookups, hidden lookup types, client directives and pruned fields - #10683

Merged
michaelstaib merged 4 commits into
mainfrom
mst/fusion-composition-gateway
Oct 25, 2026
Merged

michaelstaib merged 4 commits into
mainfrom
mst/fusion-composition-gateway

Conversation

@michaelstaib

@michaelstaib michaelstaib commented Oct 25, 2026 •

Copy link
Copy Markdown
Member

Composition now accepts node lookups where a type's key is marked @external in another schema that has a node lookup. The gateway no longer fails to start when a lookup returns a type hidden with @inaccessible. Client directives that every source schema defines are now kept in the composite schema and sent on to the sources. Composition now fails when pruning removes a field that no other schema serves while its type is still reachable. This builds on #10682.

@github-actions github-actions Bot added 📚 documentation This issue is about working on our documentation. 🌶️ hot chocolate labels Oct 25, 2026
@michaelstaib
michaelstaib force-pushed the mst/fusion-composition-gateway branch from 07d4f95 to bb64009 Compare October 25, 2026 06:28
@michaelstaib
michaelstaib marked this pull request as ready for review October 25, 2026 06:28
@michaelstaib
michaelstaib added this pull request to stack #10684 October 25, 2026 07:06
@michaelstaib
michaelstaib force-pushed the mst/fusion-composition-gateway branch from bb64009 to d765e22 Compare October 25, 2026 07:09
Base automatically changed from mst/fusion-requirement-planning to main October 25, 2026 07:32
…l in another node lookup schema

With source schema node resolution, satisfiability required every source
node lookup to resolve every field of each possible type it declares. A
type whose key is @external in one node lookup schema was rejected even
though the planner resolves each possible type through any of its node
lookups and picks the key owner.

When no field of the Node interface is uniformly owned, so a query cannot
bind all node branches to one source, a possible type that some node lookup
fully resolves is now validated only through the node lookups that fully
resolve it. An @external key field is still not served by its declaring
schema and is still not key data for a transition out of it.

The relaxation applies to source schema node resolution only. Gateway node
resolution still validates each possible type through every by-id lookup
that returns it, because the planner only tries the schemas that serve part
of a node branch selection there.
…ible type

Lookup completion resolved the lookup's return type through the public type
view, which throws for an @inaccessible type. An Apollo Federation entity
interface with an @inaccessible implementor gets a key lookup for that
implementor, so the gateway could not build its schema from a composition it
had accepted ("A type with the name `Private` does not exist"). The return
type is now resolved like the declaring type, including inaccessible types.

The planner topology cache resolved the inline fragment type conditions of
lookup requirements the same way. An abstract lookup whose @is map names an
inaccessible implementor (`@is(field: "<Cat>.id | <Dog>.id")` with
`Dog @inaccessible`) let the gateway start and then failed the first plan, and
every plan after it, with "A type with the name `Dog` does not exist". These
type conditions now include inaccessible types as well.
… schema defines

The merger only merged a fixed set of well-known directive definitions, and
pruning removed every directive definition the schema did not apply, so an
executable directive such as `directive @marker(note: String!) on FIELD` never
reached the composite schema and the gateway rejected operations using it.

A client directive is now merged when every source schema defines it. The
merged definition keeps the shared locations among FIELD and
VARIABLE_DEFINITION, the locations at which the gateway passes the directive
on with the field or variable that carries it. It is repeatable only when
every definition is, and keeps the arguments every definition declares with
the most restrictive type and a default only when all defaults agree. The
directive is left out when a source schema lacks it, when no location is
shared, when argument types cannot be merged, or when a source schema
requires an argument another one does not accept. Spec directives and
@defer and @stream are not merged.

Pruning runs on each source schema before validation, and a directive it
keeps also keeps its argument types for validation and merging. Pruning
therefore keeps a client directive only when every source schema defines it
with a shared forwarded location, and every type its arguments reach,
directly or through input object fields, has the same definition without
@inaccessible elements in every source schema that defines it. Any other
client directive is pruned with its argument types as before, so a client
directive never makes composition fail. For example, `@format(style: Style)`
with `enum Style { A B }` in one source schema and `enum Style { A B C }` in
another is left out instead of failing with ENUM_VALUES_MISMATCH.
…a serves

Per-source pruning removes object and interface types that a source
schema cannot reach from its root types. When clients can still reach
such a type in the composite schema, its fields that no other source
schema resolves disappeared from the composite schema without a
diagnostic.

The pruning pass now returns the types it removes, and SchemaComposer
records the removed object and interface types on each source schema.
The new UnreachableSourceFieldRule post-merge rule reports
UNREACHABLE_SOURCE_FIELD for every field of such a type when clients can
reach the type in the composite schema and no other source schema
contributes a non-partial declaration of the field. A field of an
interface also counts as resolved when every reachable implementation
resolves it. External, internal, inaccessible and overridden
declarations are skipped.

Client reachability is computed once per composition, and only when a
removed type is part of the merged schema, by walking the merged schema
from its root types through accessible fields, implemented interfaces,
the implementations of interface-typed fields and union members. Types
that clients cannot reach, including @inaccessible types and types
reached only through @inaccessible fields, stay silently removed.
@michaelstaib
michaelstaib force-pushed the mst/fusion-composition-gateway branch from d765e22 to d836a13 Compare October 25, 2026 07:34
@michaelstaib
michaelstaib merged commit f58ad43 into main Oct 25, 2026
302 of 305 checks passed
@michaelstaib
michaelstaib deleted the mst/fusion-composition-gateway branch October 25, 2026 07:49
This was referenced Oct 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📚 documentation This issue is about working on our documentation. 🌶️ hot chocolate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant