Working with Entities
Use Apollo Connectors to enrich and reference entities using REST APIs
Apollo Connectors are designed to simplify working with entities in federated graphs. An entity is any object that can identified with one or more unique key fields, much like rows in a database table. The different data fields are like columns in the table. GraphQL federation uses entity types to represent object types that combine information from multiple data sources.
Identifying entities
Most APIs associate entities with particular business domains.
When working with REST APIs, if there's a GET
endpoint with a unique identifier (for example, /products/:id
) it's likely you're dealing with an entity.
Retrieve entity fields with Connectors
When retrieving an entity with a Connector, you use the @connect
directive on the type instead of a field on Query
.
1type Product
2 @connect(
3 source: "ecomm"
4 http: { GET: "/products/{$this.id}" }
5 selection: """
6 id
7 name
8 description
9 """
10 )
11{
12 id: ID!
13 name: String
14 description: String
15}
In this example, the query planner can fetch Product fields using this Connector as long as it has a value for the Product's id
field, as indicated by $this.id
.
N+1 problem.
Learn more about batching requests.Where to use @connect
You can apply the @connect
directive in two ways when working with entities:
On entity types - Use this when you want to fetch entity fields without exposing a dedicated query to fetch entities directly
On
Query
fields - Use this when you need both a way to fetch entities via a root field and to fetch entity fields from a query for another type
Connectors on Query
fields are just as common as Connectors on types or fields of types. Choose the approach that best fits your specific use case and keeps your schema clean.
@connect
on entity types
When you only need to enhance an entity with additional fields without creating a root field to access it, apply @connect
directly on the type. For example, if you only need to fetch products' prices in the context of fetching product information, but never fetch prices in isolation:
1type Product {
2 id: ID!
3 name: String
4 price: Price
5}
6
7type Price @key(fields: "id")
8 @connect(
9 source: "ecomm"
10 http: { GET: "/products/{$this.id}/price" }
11 selection: """
12 id
13 active
14 currency
15 unitAmount
16 }
17 """
18 ){
19 id: ID!
20 active: Boolean
21 currency: String
22 unitAmount: Float
23}
24
25type Query {
26 products: [Product]
27 @connect(
28 source: "ecomm"
29 http: { GET: "/products"}
30 selection: """
31 $.products {
32 id
33 name
34 price {
35 id: default_price
36 }
37 }
38 """
39 )
40}
This pattern is cleaner than the legacy approach of creating an @inaccessible
field with entity: true
.
Legacy approach
@connect
to fields and use entity: true
to resolve entities:1type Price @key(fields: "id") {
2 id: ID!
3 active: Boolean
4 currency: String
5 unitAmount: Float
6}
7
8type Query {
9 price(id: ID!): Price
10 @connect(
11 source: "ecomm"
12 http: { GET: "/prices/{$args.id}"}
13 selection: """
14 id
15 active
16 currency
17 unitAmount: unit_amount
18 """
19 entity: true
20 )
21 @inaccessible
22}
entity: true
indicates that this Connector should also be used to resolvePrice
references@inaccessible
is used to hide this query field from clients while still allowing it to resolve entity references
entity: true
.@connect
on Query
When you need both a way for clients to directly query for entities and to resolve entity references, place the @connect
directive on a Query
field with entity: true
.
1type Query {
2 product(id: ID!): Product
3 @connect(
4 source: "ecomm"
5 http: { GET: "/products/{$args.id}" }
6 selection: """
7 id
8 name
9 description
10 """
11 entity: true
12 )
13}
14
15type Product {
16 id: ID!
17 name: String
18 description: String
19}
This approach avoids duplicating a Connector when you need both capabilities. This pattern has some limitations as described in Rules for entity: true
.
Additional resources
For more foundational information about entities:
See the entity documentation for additional information on using entities in GraphQL Federation.
Check out the Thinking in Entities blog post for best practices for using entities with Connectors.
To learn more advanced entity resolution patterns:
See Entity Resolution Patterns to learn how to combine multiple endpoints and optimize data fetching
See Working with Entities Across Subgraphs to learn how to work with entities in a federated architecture
Learn about batching requests to optimize entity resolution and avoid the N+1 problem