diff --git a/docs/source/entities-advanced.mdx b/docs/source/entities-advanced.mdx index a9d24b623..2dbf14cfe 100644 --- a/docs/source/entities-advanced.mdx +++ b/docs/source/entities-advanced.mdx @@ -4,15 +4,41 @@ title: Advanced topics on federated entities This article describes complex behaviors of federated entities beyond those covered in [entity basics](./entities/). -## Advanced `@key`s +## Using advanced `@key`s -A single entity can have multiple `@key`s. Additionally, a `@key` can include multiple fields, and even arbitrarily nested fields. +Depending on your entities' fields and usage, you may need to use more advanced `@key`s. For example, you may need to define a [compound `@key`](#compound-keys) if multiple fields are required to uniquely identify an entity. If different subgraphs interact with different fields an entity, you may need to define [multiple](#multiple-keys)—and sometimes [differing](#differing-keys-across-subgraphs)—`@key`s for the entity. + +### Compound `@key`s + +A single `@key` can consist of multiple fields, the combination of which uniquely identifies an entity. This is called a **compound** or composite key. In the following example, the combination of both `username` and `domain` fields is required to uniquely identify the `User` entity: + +```graphql {1} title="Users subgraph" +type User @key(fields: "username domain") { + username: String! + domain: String! +} +``` + +#### Nested fields in compound `@key`s + +Compound keys can also include _nested_ fields. In the following example, the `User` entity's primary key consists of both a user's `id` _and_ the `id` of that user's associated `Organization`: + +```graphql {1} title="Users subgraph" +type User @key(fields: "id organization { id }") { + id: ID! + organization: Organization! +} + +type Organization { + id: ID! +} +``` ### Multiple `@key`s -You can define more than one `@key` for an entity, when applicable. +When different subgraphs interact with different fields of an entity, you may need to define multiple `@key`s for the entity. For example, a Reviews subgraph might refer to products by their ID, whereas an Inventory subgraph might use SKUs. -In this example, a `Product` entity can be uniquely identified by either its `id` _or_ its `sku`: +In the following example, the `Product` entity can be uniquely identified by _either_ its `id` _or_ its `sku`: ```graphql {1} title="Products subgraph" type Product @key(fields: "id") @key(fields: "sku") { @@ -22,10 +48,34 @@ type Product @key(fields: "id") @key(fields: "sku") { price: Int } ``` +
+ +**Note:** If you include multiple sets of `@key` fields, the query planner uses the most efficient set for entity resolution. For example, suppose you allow a type to be identified by `@key(fields: "id")` _or_ `@key(fields: "id sku")`: + +```graphql {1} +type Product @key(fields: "id") @key(fields: "id sku") { + # ... +} +``` + +That means either `id` or (`id` _and_ `sku`) is enough to uniquely identify the entity. Since `id` alone is enough, the query planner will use only that field to resolve the entity, and `@key(fields: "id sku")` is effectively ignored. + +-This pattern is helpful when different subgraphs interact with different fields of an entity. For example, a Reviews subgraph might refer to products by their ID, whereas an Inventory subgraph might use SKUs. +#### Referencing entities with multiple keys -A subgraph that [references an entity without contributing any fields](./entities/#referencing-an-entity-without-contributing-fields) can include the fields of any `@key` in its stub definition: +A subgraph that [references an entity without contributing any fields](./entities/#referencing-an-entity-without-contributing-fields) can use any `@key` fields in its stub definition. For example, if the Products subgraph defines the `Product` entity like this: + +```graphql {1} title="Products subgraph" +type Product @key(fields: "id") @key(fields: "sku") { + id: ID! + sku: String! + name: String! + price: Int +} +``` + +Then, a Reviews subgraph can use either `id` or `sku` in the stub definition: ```graphql title="Reviews subgraph" # Either: @@ -40,28 +90,9 @@ type Product @key(fields: "sku", resolvable: false) { ``` -### Compound `@key`s - -A single `@key` can consist of multiple fields, and even _nested_ fields. - -In this example, the `User` entity's primary key consists of both a user's `id` _and_ the `id` of that user's associated `Organization`: - -```graphql {1} title="Users subgraph" -type User @key(fields: "id organization { id }") { - id: ID! - organization: Organization! -} - -type Organization { - id: ID! -} -``` - ### Differing `@key`s across subgraphs -An entity often has the exact same `@key` field(s) across subgraphs, but this isn't required. For example, you can define a `Product` entity shared between subgraphs, one with `sku` and `upc` as the `@key`s and one with only `upc` as the `@key` field: - -
❌
@@ -105,9 +133,9 @@ type Product @key(fields: "upc") { -#### Operations with mismatched `@key`s +#### Operations with differing `@key`s -Mismatched keys affect which fields from an entity can be resolved. Requests can resolve an entity's fields _if there is a traversable path from the root query to the fields_. +Differing keys across subgraphs affect which of the entity's fields can be resolved from each subgraph. Requests can resolve fields **if there is a traversable path from the root query to the fields**. Take these subgraph schemas as an example: @@ -136,9 +164,9 @@ type Product @key(fields: "upc") { ``` -The queries defined in the products subgraph can always resolve all product fields because the product entity can be joined via the `upc` field present in both schemas. +The queries defined in the Products subgraph can always resolve all product fields because the product entity can be joined via the `upc` field present in both schemas. -On the other hand, queries added to the inventory subgraph can't resolve fields from the products subgraph: +On the other hand, queries added to the Inventory subgraph can't resolve fields from the Products subgraph: