Contribute and Reference Entity Fields

Contribute and reference entity fields across subgraphs


In a federated GraphQL architecture, individual subgraphs can contribute to and reference fields from shared entities. This guide explains how subgraphs work together to build a cohesive schema, with examples of contributing and computing entity fields, as well as referencing entities without contributing fields.

Contributing entity fields

Any number of different subgraphs can contribute fields to an entity definition. In the example below, the Products and Inventory subgraphs contribute different fields to the Product entity:

GraphQL
Products
1type Product @key(fields: "id") {
2  id: ID!
3  name: String!
4  price: Int
5}
GraphQL
Inventory
1type Product @key(fields: "id") {
2  id: ID!
3  inStock: Boolean!
4}

By default, each subgraph must contribute different fields, with the important exception of @key fields. If multiple subgraphs attempt to contribute the same field, a composition error occurs. To override this default behavior, see Resolving another subgraph's field .

Each subgraph that contributes fields to an entity must define a reference resolver for that entity.

Contributing computed entity fields

You can define entity fields that are computed from values of other entity fields, even when a different subgraph resolves those fields.

For example, this Shipping subgraph adds a shippingEstimate field to the Product entity. This field is calculated based on the product's size and weight, which the Products subgraph defines:

GraphQL
Shipping
1type Product @key(fields: "id") {
2  id: ID!
3  size: Int @external
4  weight: Int @external
5  shippingEstimate: String @requires(fields: "size weight")
6}
GraphQL
Products
1type Product @key(fields: "id") {
2  id: ID!
3  name: String!
4  price: Int
5  size: Int
6  weight: Int
7}

Notice the Shipping subgraph uses two directives:

  • The @requires directive indicates which fields are required from other subgraphs.

  • The @external directives is applied to required fields in the type definition.

    • This directive tells the router, "This subgraph knows these fields exist, but it can't resolve them itself."

How the router processes computed entity fields

In the previous example, if a query requests a product's shippingEstimate, the router does the following:

  1. It queries the Products subgraph for the product's size and weight.

  2. It queries the Shipping subgraph for the product's shippingEstimate.

  • It includes the size and weight of the Product object passed to the resolver for shippingEstimate:

JavaScript
resolvers.js
1{
2  Product: {
3    shippingEstimate(product) {
4      return computeShippingEstimate(product.id, product.size, product.weight);
5    }
6  }
7}

Using @requires with object subfields

If a computed field @requires a field that returns an object type, you also specify which subfields of that object are required. You list those subfields with the following syntax:

GraphQL
Shipping
1type Product @key(fields: "id") {
2  id: ID!
3  dimensions: ProductDimensions @external
4  shippingEstimate: String @requires(fields: "dimensions { size weight }")
5}

In this modification of the previous example, size and weight are now subfields of a ProductDimensions object. The Products and Shipping subgraphs must both define the ProductDimensions type for this to be valid.

Using @requires with fields that take argumentsSince 2.1.2

Starting in Federation v2.1.2, the @requires directive can include fields that take arguments, like so:

GraphQL
Shipping
1type Product @key(fields: "id") {
2  id: ID!
3  weight(units: String): Int @external
4  #highlight-start
5  shippingEstimate: String @requires(fields: "weight(units:\"KILOGRAMS\")")
6  #highlight-end
7}

The following rules apply:

  • The router provides the specified values in its query to whichever subgraph defines the required field.

  • Each specified argument value is static; the router always provides the same value.

  • You can omit values for nullable arguments. You must provide values for non-nullable arguments.

  • If you define your subgraph schema in an SDL file instead of programmatically, you must escape quotes for string and enum values with backslashes (as shown above).

Referencing an entity without contributing fields

Your subgraphs can use an entity as a field's return type without contributing any fields to that entity.

For example, take a look at this Product entity in the Products subgraph:

GraphQL
Products
1type Product @key(fields: "id") {
2  id: ID!
3  name: String!
4  price: Int
5}

Suppose you want to create a Reviews subgraph that includes the following Review type:

GraphQL
Reviews
1type Review {
2  product: Product!
3  score: Int!
4}

While this is possible, the current Reviews subgraph schema is invalid because it doesn't define the Product entity.

To fix this, add a stub of the Product entity to the Reviews schema, like so:

GraphQL
Reviews
1type Review {
2  product: Product!
3  score: Int!
4}
5
6type Product @key(fields: "id", resolvable: false) {
7  id: ID!
8}

A stub definition includes only the @key fields of an entity. In this case, the Product type definition only includes the id field. It also includes resolvable: false in the @key directive to indicate that this subgraph doesn't define a reference resolver for the Product entity.