Skip to content

Instantly share code, notes, and snippets.

@JJediny
Created November 7, 2025 20:52
Show Gist options
  • Save JJediny/9c98e9307538e66bd0bcb22e7d2a037d to your computer and use it in GitHub Desktop.
Save JJediny/9c98e9307538e66bd0bcb22e7d2a037d to your computer and use it in GitHub Desktop.

A Concise Guide to GraphQL Federation Directives

GraphQL Federation allows you to build a single, unified data graph by combining multiple backend services (subgraphs). The gateway intelligently plans and executes queries across these services. This guide covers the core directives and concepts that make it work.

We'll use a simple e-commerce example with three subgraphs:

  • Users Service: Manages user data (id, name).
  • Products Service: Manages product data (upc, price, name).
  • Reviews Service: Manages reviews, which are linked to users and products.

๐ŸŸข Core Concepts: The Building Blocks

These are the absolute essentials for making federation work.

1. _service field

  • What it is: A query field automatically added to your subgraph schema that returns its Schema Definition Language (SDL).
  • Why you use it: It's the "heartbeat" of federation. The gateway polls this field on each subgraph to fetch its schema and capabilities, which it then composes into the final supergraph.
  • Example Usage: You don't add this to your schema yourself; the federation library does it. You can query it to verify a service is running correctly.
    # Query sent by the gateway
    query {
      _service {
        sdl
      }
    }

2. @key (Single Field)

  • What it is: A directive that designates a field (or set of fields) as the unique primary key for an entity.
  • Why you use it: It tells the gateway, "This object type is an entity, and you can use this key to look it up." This allows other services to reference and extend the entity.
  • Example (in the users service):
    # users-subgraph/schema.graphql
    type User @key(fields: "id") {
      id: ID!
      name: String!
    }
    This tells the gateway that any other subgraph can get a User if it knows the id.

3. @key (Composite / Multi-Field)

  • What it is: A @key directive that uses a combination of multiple fields to form a unique primary key.
  • Why you use it: Use this when a single field isn't enough to uniquely identify an object.
  • Example (in the products service): A product might be unique only by its UPC within a specific tenantId.
    # products-subgraph/schema.graphql
    type Product @key(fields: "upc tenantId") {
      upc: ID!
      tenantId: ID!
      name: String
    }

4. repeatable @key

  • What it is: The ability to define multiple, alternative keys for a single entity.
  • Why you use it: Allows an entity to be fetched using different unique identifiers. For example, a product could be found by its upc OR its sku.
  • Example (in the products service):
    # products-subgraph/schema.graphql
    type Product @key(fields: "upc") @key(fields: "sku") {
      upc: ID!
      sku: String!
      name: String
    }

๐Ÿš€ Federation v2+ Concepts

These directives were introduced in Apollo Federation 2 to provide more power and flexibility. You must use @link to enable them.

5. @link

  • What it is: A directive that links your subgraph's schema to the official Federation specification, enabling Federation 2 features.
  • Why you use it: It's the entry point for all Federation 2 features like @shareable, @override, @interfaceObject, etc.
  • Example (at the top of your schema):
    # Add this to the top of every subgraph schema
    extend schema
      @link(url: "https://specs.apollo.dev/federation/v2.3",
            import: ["@key", "@requires", "@provides", "@shareable", "@override", ...])
    
    type Product @key(fields: "upc") {
      # ...
    }

โš™๏ธ Inter-Service Communication & Schema Management

These directives manage how data and types are shared, extended, and resolved across subgraphs.

6. @requires

  • What it is: A directive on a computed field that indicates it needs other external fields to be resolved.
  • Why you use it: To calculate a field in one service using data owned by another service. The gateway ensures the required fields are fetched first.
  • Example: The users service stores name and email. The reviews service wants to compute a reviewerHandle like "Jane D. ([email protected])".
    # reviews-subgraph/schema.graphql
    extend type User @key(fields: "id") {
      id: ID! @external
      name: String @external # from the users service
      email: String @external # from the users service
    
      # This field is computed here, but needs data from the users service
      reviewerHandle: String! @requires(fields: "name email")
    }
    The gateway will first fetch name and email from the users service before calling the reviewerHandle resolver in the reviews service.

7. @provides

  • What it is: A directive on a field that returns an entity, indicating that its resolver will also provide other fields for that entity.
  • Why you use it: To optimize queries by fetching related data in a single resolver trip instead of letting the gateway make a second network call.
  • Example: The reviews service owns reviews. A Review has an author (a User). The reviews service can probably fetch the user's name at the same time it fetches the review, saving a hop to the users service.
    # reviews-subgraph/schema.graphql
    type Review @key(fields: "id") {
      id: ID!
      body: String!
      # When you resolve 'author', you also provide the 'name'
      author: User! @provides(fields: "name")
    }
    
    extend type User @key(fields: "id") {
      id: ID! @external
      name: String @external
    }

8. @shareable

  • What it is: Marks a field or object type definition as being defined and resolved identically across multiple subgraphs.
  • Why you use it: To prevent composition errors when multiple subgraphs define the same simple, non-entity type (like a Timestamp object) or the same field on a shared entity. It tells the gateway, "Don't worry, these are the same thing."
  • Example: Both products and reviews services want to use a Timestamp type.
    # In both products-subgraph and reviews-subgraph
    type Timestamp @shareable {
      iso8601: String!
      unix: Int!
    }
    
    type Product @key(fields: "upc") {
      upc: ID!
      createdAt: Timestamp @shareable # Field is also shareable
    }

9. @override

  • What it is: Allows a subgraph to take over the responsibility (ownership) of a field that was originally defined in another subgraph.
  • Why you use it: Essential for migrating fields between services without downtime. You can deploy a new service that @overrides a field, and the gateway will start routing requests for that field to the new service.
  • Example: The products service originally owned name. A new marketing service will now be the source of truth for product names.
    # marketing-subgraph/schema.graphql
    extend type Product @key(fields: "upc") {
      upc: ID! @external
      # I am now the owner of the 'name' field, overriding the 'products' service
      name: String @override(from: "products")
    }

10. @inaccessible

  • What it is: Hides a field or type from the final supergraph schema, making it invisible to clients but still available for use within its own subgraph (e.g., for @requires).
  • Why you use it: To hide implementation details or internal-only fields that other services might need for computation but clients should not see.
  • Example: The products service has an internal ID that the reviews service needs, but it shouldn't be public.
    # products-subgraph/schema.graphql
    type Product @key(fields: "upc") {
      upc: ID!
      # This ID is for internal use only and won't be in the supergraph
      internalDatabaseId: ID! @inaccessible
    }

Operational & Advanced Concepts

federated tracing

This is not a directive but an operational feature. When enabled, the gateway includes detailed performance and query plan information in the extensions of a GraphQL response. This allows tools like Apollo Studio to visualize exactly how a query was executed across all your subgraphs, helping you debug performance bottlenecks.


Summary Cheat Sheet

Directive / Concept Purpose Federation Version
_service The "heartbeat" query for the gateway to fetch a subgraph's schema (SDL). v1+
@key Declares an object type as an "entity" with a primary key. The foundation of federation. v1+
@requires Specifies that a field's resolver needs external fields from another service. v1+
@provides Optimizes queries by resolving fields from another entity in the same service call. v1+
@link Links to the Federation spec to enable v2 features. Required for all below. v2+
@shareable Prevents composition errors by marking a field/type as identical across subgraphs. v2+
@override Allows a subgraph to take ownership of a field from another subgraph (for migration). v2+
@inaccessible Hides a field from the public supergraph but keeps it for internal use. v2+
@tag Adds metadata labels to parts of your schema for external tools (e.g., contracts). v2+
@interfaceObject Allows a subgraph to implement an entity interface that it doesn't own. v2+
@composeDirective Allows you to apply a custom directive in a subgraph and have it composed into the supergraph. v2+
federated tracing An operational feature (not a directive) for deep performance monitoring. v1+
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment