Thinking in entities
Best practices for designing your schema with entities
💡 TIP
If you're an enterprise customer looking for more material on this topic, try the
Not an enterprise customer?
Define, reference, and extend entities as needed
The @key
directive to its definition in a subgraph schema. The @key
directive defines a unique key for the entity and its fields
argument will contain one or more of the type's fields. In the following example, the Product
entity's primary key would be its upc
field:
type Product @key(fields: "upc") {upc: String!name: String!description: String}
Setting the upc
field as the key means that other subgraphs that want to use this entity will need to know at least that value for any product. The keys we define should be values that uniquely identify a resource. This is because we want to avoid scenarios where they are used to arbitrarily pass dynamic field data around query execution between subgraphs.
After defining an entity in a schema, other subgraphs can reference that entity in their schemas. In order for the referencing subgraph's schema to be valid, it must define a stub of the entity in its schema. For example, we can reference a Product
type defined in the products subgraph as the return type corresponding to a product
field on a Review
type defined in reviews subgraph:
type Review {rating: Intproduct: Product}type Product @key(fields: "upc") {upc: String!}
The @key
directive indicates that the reviews subgraph will be able to identify a product by its UPC value and therefore be able to connect to a product based on its upc
primary key field, but the reviews subgraph does not need to be aware of any other details about a given product.
Referencing entities is a key feature of federation, but it's only half of the story. While an entity will be owned by a single subgraph, other subgraphs might wish to add additional fields to the entity's type to provide a more holistic representation of the entity in the graph. Doing so is a simple as adding the additional field to the extended type in a non-originating subgraph. For example, a reviews subgraph's schema might add a reviews
field to the extended Product
type that was originally defined in the products subgraph:
type Review {rating: Intproduct: Product}type Product @key(fields: "upc") {upc: String!reviews: [Review]}
When extending entities, it's important to keep in mind that the entity's originating subgraph will not be aware of the added fields. Additionally, each field in an entity must only be defined once or the gateway will encounter schema composition errors.
Work from the entities outward
When migrating from a client-only or monolithic GraphQL pattern, that work begins by identifying what entities will be exposed in the first subgraph extracted from the larger schema. When migrating from an architecture consisting of BFF-based GraphQL APIs or any other architecture of multiple overlapping graphs, the work of identifying entities (and determining new subgraph boundaries, in general) might be a bit more complex and involve some degree of negotiation with respect to type ownership, as well as a migration process to help account for any breaking changes that might result for clients.
Whatever your architectural starting point, Apollo Federation was designed to allow the work of identifying entities and defining subgraph boundaries to be done in an incremental, non-disruptive fashion. Beginning to identify these entities is also the essential prerequisite for adopting the other schema design best practices that will follow.
For more information on entity usage and capabilities, please see the
@defer
and entities
@defer
and entitiesEntities aren't just useful for connecting data across subgraphs. You can also use entities to enable the new @defer
directive for client-controlled prioritization of response data