Docs
Launch GraphOS Studio

Federation-specific GraphQL directives


defines a collection of directives that you use in your to enable certain features.

Importing directives

To use federated in a Federation 2 , apply the @link with the following format to the schema type:

extend schema
@link(url: "https://specs.apollo.dev/federation/v2.3",
import: ["@key", "@shareable"])

You can apply this to your existing schema declaration if you have one, or to a new extend schema declaration (as shown above).

Modify the import array to include whichever federated your uses. The example above imports the @key and @shareable (which are used most commonly).

💡 TIP

Make sure to include the @ in each name.

Renaming directives

If an imported 's default name matches one of your own custom directives, you can rename the imported directive with the following syntax:

extend schema
@link(url: "https://specs.apollo.dev/federation/v2.3",
import: [{ name: "@key", as: "@uniqueKey"}, "@shareable"])

This example uses @uniqueKey for the federated usually named @key.

Namespaced directives

If you don't import a particular directive from a linked spec, you can still use that in your . However, that directive is namespaced with a prefix:

extend schema
@link(url: "https://specs.apollo.dev/federation/v2.3",
import: ["@key"])
type Book @federation__shareable {
title: String!
}

In the example above, @shareable is not imported from the federation spec. Therefore, it is available as @federation__shareable.

The default namespace prefix for a @linked is the name of its associated specification (indicated by the penultimate component of url), plus two underscores (__). For , this prefix is federation__.

You can customize a particular specification's namespace prefix by providing the as to @link:

extend schema
@link(url: "https://specs.apollo.dev/federation/v2.3",
as: "fed")
type Book @fed__shareable {
title: String!
}

As shown, custom namespace prefixes also end in two underscores.

directive @link(
url: String!,
as: String,
for: link__Purpose,
import: [link__Import]
) repeatable on SCHEMA

This links definitions from an external specification to this schema. Every Federation 2 uses the @link to import the other federation-specific described in this article (see the syntax in Importing directives).

For more information on @link, see the official spec.

Managing types

@key
Since 1.0

directive @key(fields: FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE

Designates an as an entity and specifies its key fields (a set of that the can use to uniquely identify any instance of the ).

type Product @key(fields: "id") {
id: ID!
name: String!
price: Int
}

You can apply multiple @key to a single (to specify multiple valid sets of key ), if your library supports repeatable :

type Product @key(fields: "upc") @key(fields: "sku") {
upc: ID!
sku: ID!
name: String
}

To check whether your library supports repeatable , see the repeatable @key item in Federation-compatible subgraph implementations.

In Apollo Federation 2.3 and later, you can also apply @key to interface definitions to create entity interfaces. If you apply @key to an interface in earlier versions of Federation 2, a error occurs.

Arguments

Name /
Type
Description
fields

FieldSet!

Required. A selection set (provided as a string) of and subfields that contribute to the 's unique key.

Examples:

  • "id"
  • "username region"
  • "name organization { id }"

See also Advanced @keys.

resolvable

Boolean

If false, indicates to the that this doesn't define a reference resolver for this . This means that plans can't "jump to" this to resolve that aren't defined in another .

Most commonly, you set this to false when referencing an entity without contributing fields.

The default value is true.

directive @interfaceObject on OBJECT

Indicates that an object definition serves as an abstraction of another 's entity interface. This abstraction enables a to automatically contribute to all entities that implement a particular interface.

During , the of every @interfaceObject are added both to their corresponding interface definition and to all types that implement that interface.

@extends
Since 1.0

directive @extends on OBJECT | INTERFACE

Indicates that an object or interface definition is an extension of another definition of that same type.

⚠️ If your subgraph library supports GraphQL's built-in extend keyword, do not use this directive! Instead, use extend.

This is for use with libraries that do not support the extend keyword. Most commonly, these are libraries that generate their schema programmatically instead of using a static .graphql file.

Note that Federation 2 does not require any use of type .

In Federation 1, every must extend the Query and Mutation types (if it defines them), and entities are extended in every that defines them except their originating .

Managing shared fields

@shareable
Since 2.0

directive @shareable on FIELD_DEFINITION | OBJECT

Indicates that an 's is allowed to be resolved by multiple (by default in Federation 2, object fields can be resolved by only one subgraph).

type Position {
x: Int! @shareable
y: Int! @shareable
}

If applied to an definition, all of that type's are considered @shareable:

type Position @shareable {
x: Int!
y: Int!
}

If a is marked @shareable in any , it must be marked as either @shareable or @external in every Federation 2 that defines it.

If a Federation 2 includes a Federation 1 , all value types in the Federation 1 subgraph are automatically considered @shareable by the Federation 2 algorithm.

If a is included in an 's @key directive, that is automatically considered @shareable and the is not required in the corresponding (s).

See also Value types in Apollo Federation and Resolving another subgraph's field.

The @shareable is about indicating when an object can be resolved by multiple . As interface fields are not directly resolved (their implementation is), @shareable is not meaningful on an interface and is not allowed (at least since federation 2.2; earlier versions of federation 2 mistakenly ignored @shareable on interface ).

@inaccessible
Since 2.0

directive @inaccessible on FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION

Indicates that a definition in the should be omitted from the 's API schema, even if that definition is also present in other subgraphs. This means that the is not exposed to clients at all.

Common use cases for @inaccessible include:

  • Avoiding errors while making staggered updates to a definition that's shared across multiple (such as a value type)
  • Using a private as part of an 's @key without exposing that to clients

⚠️ Unlike with most directives, composition preserves uses of this in the generated . To preserve uses of other , see @composeDirective.

Consequently, if you rename this directive, you must use the same name in every . Otherwise, a error occurs due to a naming mismatch.

Subgraph A
type Position @shareable {
x: Int!
y: Int!
z: Int! @inaccessible
}
Subgraph B
type Position @shareable {
x: Int!
y: Int!
# Subgraph is not yet updated
}

Often when you add a to a value type in one , fails because that isn't resolvable in other . With @inaccessible, you can preserve while adding the to your remaining . When the rollout is complete, you can remove the and begin using the field.

An @inaccessible or type is not omitted from the supergraph schema, so the still knows it exists (but clients can't include it in ). This is what enables the router to use an @inaccessible as part of an 's @key when combining from multiple .

If a type is marked @inaccessible, all fields that return that type must also be marked @inaccessible. Otherwise, a error occurs.

For more information, see Using @inaccessible.

@override
Since 2.0

directive @override(from: String!) on FIELD_DEFINITION

Indicates that an object is now resolved by this instead of another where it's also defined. This enables you to migrate a from one subgraph to another.

You can apply @override to entity and fields of the root types (such as Query and Mutation).

Products subgraph
type Product @key(fields: "id") {
id: ID!
inStock: Boolean!
}
Inventory subgraph
type Product @key(fields: "id") {
id: ID!
inStock: Boolean! @override(from: "Products")
}

In the example above, we're migrating the Product.inStock from the Products to the Inventory subgraph. The composed indicates that Product.inStock is resolved by the Inventory but not the Products , even though the Products subgraph also defines the .

You can apply @override to a @shareable . If you do, only the you provide in the from no longer resolves that . Other can still resolve the .

Only one can @override any given . If multiple attempt to @override the same , a error occurs.

For more information, see Migrating entities and fields.

Progressive @override is an Enterprise feature of the and requires an organization with a GraphOS Enterprise plan. If your organization doesn't have an , you can test it out by signing up for a free Enterprise trial.

Rolling out any change to a production , including migration, risks degrading the performance of your graph. Rerouting all traffic from one subgraph to another all at once could overload the overriding .

The progressive @override feature enables the gradual, progressive deployment of a with an @override . As a developer, you can customize the percentage of traffic that the overriding and overridden subgraphs each resolve for a field. You apply a label to an @override to set the percentage of traffic for the field that should be resolved by the overriding , with the remaining percentage resolved by the overridden subgraph. You can then monitor the performance of the subgraphs in Studio, resolve any issues, and iteratively and progressively increase the percentage until all traffic is resolved by the overriding subgraph.

To learn more, see the Incremental migration with @override guide.

Arguments

Name /
Type
Description
from

String!

Required. The name of the other that no longer resolves the .

  • If you're performing composition with managed federation, this must match the name of the registered to .
  • If you're performing composition with the Rover CLI, this must match the name of the in the YAML config file you provide to rover supergraph compose.
label

String

This is available in 2.7 and later. It is an Enterprise feature of the and requires an organization with a GraphOS Enterprise plan. You can test it out by signing up for a free Enterprise trial.

Optional. A string of arbitrary . Supported in this release:

  • percent(<percent-value>) - The percentage of traffic for the that's resolved by this . The remaining percentage is resolved by the other (from) . To learn more, see Incremental migration with @override.

Controlling access

This is an Enterprise feature of the and requires an organization with a GraphOS Enterprise plan. If your organization doesn't have an , you can test it out by signing up for a free Enterprise trial.

directive @authenticated on
FIELD_DEFINITION
| OBJECT
| INTERFACE
| SCALAR
| ENUM

Indicates to that the target element is accessible only to the authenticated users. For more granular access control, see the @requiresScopes below. Refer to the Apollo Router article for additional details.

This is an Enterprise feature of the and requires an organization with a GraphOS Enterprise plan. If your organization doesn't have an , you can test it out by signing up for a free Enterprise trial.

directive @requiresScopes(scopes: [[federation__Scope!]!]!) on
FIELD_DEFINITION
| OBJECT
| INTERFACE
| SCALAR
| ENUM

Indicates to that the target element is accessible only to the authenticated users with the appropriate JWT scopes. Refer to the Apollo Router article for additional details.

Arguments

Name /
Type
Description
scopes

[federation__Scope!]!

Required. List of JWT scopes that must be granted to the user in order to access the underlying element data.

@policy
Since 2.6

This is an Enterprise feature of the and requires an organization with a GraphOS Enterprise plan. If your organization doesn't have an , you can test it out by signing up for a free Enterprise trial.

directive @policy(policies: [[federation__Policy!]!]!) on
| FIELD_DEFINITION
| OBJECT
| INTERFACE
| SCALAR
| ENUM

Indicates to that the target element is restricted based on authorization policies that are evaluated in a Rhai script or coprocessor. Refer to the Apollo Router article for additional details.

Arguments

Name /
Type
Description
policies

[federation__Policy!]!

Required. List of authorization policies to evaluate.

Referencing external fields

@external
Since 1.0

directive @external on FIELD_DEFINITION | OBJECT

Indicates that this usually can't resolve a particular object , but it still needs to define that field for other purposes.

This is always used in combination with another directive that references object , such as @provides or @requires.

Inventory subgraph
type Product @key(fields: "id") {
id: ID!
name: String! @external
inStock: Boolean!
}
type Query {
outOfStockProducts: [Product!]! @provides(fields: "name")
discontinuedProducts: [Product!]!
}

This example usually can't resolve the Product.name , but it can at the Query.outOfStockProducts path (indicated by the @provides directive).

If applied to an definition, all of that type's are considered @external:

type Position @external {
x: Int!
y: Int!
}

@provides
Since 1.0

directive @provides(fields: FieldSet!) on FIELD_DEFINITION

Specifies a set of that a can resolve, but only at a particular schema path (at other paths, the can't resolve those ).

If a can always resolve a particular , do not apply this .

Using this directive is always an optional optimization. It can reduce the total number of that your needs to communicate with to resolve certain , which can improve performance.

Inventory subgraph
type Product @key(fields: "id") {
id: ID!
name: String! @external
inStock: Boolean!
}
type Query {
outOfStockProducts: [Product!]! @provides(fields: "name")
discontinuedProducts: [Product!]!
}

This example can resolve Product.name for products returned by Query.outOfStockProducts but not Query.discontinuedProducts.

If a subgraph @provides an entity field:

  • The must define that and mark it as @external, as shown above with Product.name.
  • The must be marked as either @shareable or @external in every that defines it.
  • The must be marked as @shareable in at least one other (i.e., there's at least one subgraph that can always resolve the ).

Otherwise, a error occurs.

For more information, see Using @provides.

Arguments

Name /
Type
Description
fields

FieldSet!

Required. A selection set (provided as a string) of object and subfields that the can resolve only at this path.

Examples:

  • "name"
  • "name address"
  • "... on Person { name address }" (valid for that return a union or interface)

@requires
Since 1.0

directive @requires(fields: FieldSet!) on FIELD_DEFINITION

Indicates that the for a particular depends on the values of other that are resolved by other . This tells the that it needs to fetch the values of those externally defined first, even if the original client didn't request them.

Shipping subgraph
type Product @key(fields: "id") {
id: ID!
size: Int @external
weight: Int @external
shippingEstimate: String @requires(fields: "size weight")
}

The example above resolves a Product object's shippingEstimate , but it requires the product's size and weight to do so. Because these two are resolved by a different , they're marked as @external.

If a subgraph @requires an entity field, the must define that and mark it as @external, as shown above with Product.size and Product.weight. Otherwise, a error occurs.

See also Contributing computed entity fields.

Arguments

Name /
Type
Description
fields

FieldSet!

Required. A selection set (provided as a string) of @external object and subfields that this field requires.

Examples:

  • "name"
  • "name address"
  • "name organization { id }"

Applying metadata

@tag
Since 1.1

directive @tag(name: String!) repeatable on FIELD_DEFINITION | INTERFACE | OBJECT | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION

Applies arbitrary string metadata to a schema location. Custom tooling can use this metadata during any step of the flow, including , static analysis, and documentation. The Enterprise contracts feature uses @tag with its inclusion and exclusion filters.

⚠️ Unlike with most directives, composition preserves uses of this in the generated . To preserve uses of other , see @composeDirective.

Consequently, if you rename this directive, you must use the same name in every that uses it. Otherwise, a error occurs due to a naming mismatch.

extend schema
@link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@tag"])
type Query {
customer(id: String!): Customer @tag(name: "team-customers")
employee(id: String!): Employee @tag(name: "team-admin")
}
interface User @tag(name: "team-accounts") {
id: String!
name: String!
}
type Customer implements User @tag(name: "team-customers") {
id: String!
name: String!
}
type Employee implements User @tag(name: "team-admin") {
id: String!
name: String!
ssn: String!
}

Arguments

Name /
Type
Description
name

String!

Required. The tag name to apply.

Managing custom directives

directive @composeDirective(name: String!) repeatable on SCHEMA

Indicates to that all uses of a particular custom type system directive in the should be preserved in the (by default, omits most from the supergraph schema).

⚠️ Important: Do not use this with an executable directive. Executable have different rules for .

extend schema
@link(url: "https://specs.apollo.dev/link/v1.0")
@link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@composeDirective"])
@link(url: "https://myspecs.dev/myDirective/v1.0", import: ["@myDirective", { name: "@anotherDirective", as: "@hello" }])
@composeDirective(name: "@myDirective")
@composeDirective(name: "@hello")
directive @myDirective(a: String!) on FIELD_DEFINITION
directive @hello on FIELD_DEFINITION

This has the following requirements:

  • Ensure your subgraph library supports @composeDirective or try manually adding the @composeDirective definition to your .
  • The to preserve must be defined and imported from a core specification via the @link .
  • The specified name must match the name used for the in this subgraph.
    • If you use the as in your @link definition to modify the 's name from its spec's default, provide the modified name, not the default name.
  • If multiple import and use the :
    • The name used for the must be identical in all of those .
    • All of those should use the same major version of the spec that defines the .

If any of these requirements is not met, fails.

If different use different versions of a 's corresponding spec, the uses whichever version number is highest among all . does not verify whether this version of the is compatible with that use an earlier version.

Arguments

Name /
Type
Description
name

String!

Required. The name (including the leading @) of the to preserve during .

Previous
Composition
Next
Sharing types (value types)
Edit on GitHubEditForumsDiscord