Apollo Federation¶
Expose any schema as an Apollo Federation
subgraph. Federation::subgraph() takes a built schema and a map of entity reference
resolvers, and returns a new schema with the federation contract added:
_service { sdl }— the subgraph SDL (rendered by the schema printer)._entities(representations: [_Any!]!): [_Entity!]!— resolves entity references.- the generated
_Anyscalar,_Servicetype and_Entityunion.
use Hmennen90\GraphQL\Federation\Federation;
$subgraph = Federation::subgraph($schema, [
'User' => [
'model' => \App\Models\User::class,
'resolve' => fn (array $ref) => \App\Models\User::find($ref['id']),
],
'Product' => [
'model' => \App\Models\Product::class,
'resolve' => fn (array $ref) => \App\Models\Product::find($ref['id']),
],
]);
Wire it up as your schema via graphql.schema.factory:
// config/graphql.php
'schema' => [
'factory' => fn () => \Hmennen90\GraphQL\Federation\Federation::subgraph(
app(MySchema::class)->schema(),
[/* entity resolvers */],
),
],
How entity resolution works¶
The gateway sends _entities a list of representations — each is an object with a
__typename and the type's key fields:
query ($reps: [_Any!]!) {
_entities(representations: $reps) {
... on User { id name }
}
}
{ "reps": [{ "__typename": "User", "id": "1" }] }
For each representation the subgraph looks up the resolver registered for its
__typename, calls it with the representation array, and resolves the returned model
into the matching object type (via the _Entity union). Representations whose type has
no resolver resolve to null.
Federation v2 directives¶
The subgraph SDL returned by _service.sdl is annotated for Federation v2 — it
carries the extend schema @link(...) header and the federation directives, so an
Apollo gateway can compose it directly. Declare the directives per entity in the
config:
Federation::subgraph($schema, [
'User' => [
'model' => \App\Models\User::class,
'resolve' => fn (array $ref) => \App\Models\User::find($ref['id']),
'keys' => ['id', 'email'], // one or more @key selections
'shareable' => ['displayName'], // @shareable fields
'external' => ['legacyId'], // @external fields
'requires' => ['fullName' => 'firstName lastName'], // @requires(fields:)
'provides' => ['account' => 'plan'], // @provides(fields:)
],
]);
produces (excerpt):
extend schema
@link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@key", "@shareable", "@external", "@requires", "@provides"])
type User @key(fields: "id") @key(fields: "email") {
displayName: String @shareable
legacyId: ID @external
fullName: String @requires(fields: "firstName lastName")
}
keys defaults to "id" when omitted. The SDL is rendered by
Hmennen90\GraphQL\Engine\Schema\SchemaPrinter (the same printer behind
graphql:print); the federation plumbing (_entities, _Service, _Any) is
intentionally excluded from _service.sdl, per the federation spec.
Federation is only relevant for a distributed supergraph composed of multiple subgraphs. For a single monolithic API you do not need it.