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.

GraphQL
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.

tip
Connectors that retrieve entity fields can also use batch endpoints to resolve the 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:

GraphQL
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
Prior to v0.2 of the Connectors specification , you could only add @connect to fields and use entity: true to resolve entities:
GraphQL
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}
In this example:
  • entity: true indicates that this Connector should also be used to resolve Price references
  • @inaccessible is used to hide this query field from clients while still allowing it to resolve entity references
This pattern has some limitations as described in Rules for 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.

GraphQL
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:

To learn more advanced entity resolution patterns:

Feedback

Ask Community