Common Patterns for Apollo Connectors

Examples of how to use connectors


Preview
Apollo Connectors are currently in public preview. To get started, you need an Apollo account with a GraphOS Trial or Enterprise plan.
tip
Haven't used Apollo Connectors yet? Try it out with the Apollo Connectors Quickstart.

You can use connectors in various ways to connect your services to GraphOS. The following are some common patterns for using Connectors Directives.

Some of these patterns work across subgraphs as well. See Working with Federation for more information.

Providing root fields

The most common use of connectors is implementing Query and Mutation fields for your schema.

GraphQL
type Query {
  products: [Product]
    @connect(
      http: { GET: "https://myapi.dev/products" }
      selection: """
      $.results {
        id
        name
        price
      }
      """
    )
}
GraphQL
type Mutation {
  createProduct(input: CreateProductInput): CreateProductPayload
    @connect(
      http: {
        POST: "https://myapi.dev/products"
        body: """
        $args.input {
          name
          price
        }
        """
      }
    )
}

input CreateProductInput {
  name: String!
  price: Int!
}

Combining endpoints to complete an entity

Connectors can orchestrate multiple endpoints to provide a complete representation of a type. For example, if you have a "list" endpoint that provides a few fields and a "get" endpoint that provides more fields, you can combine them into a single type.

The important detail is marking the "get" connector with entity: true to indicate that it can use a primary key (the id field from the "list" endpoint, in this case) to resolve the entity.

GraphQL
type Query {
  products: [Product]
    @connect(
      http: { GET: "https://myapi.dev/products" }
      selection: """
      $.results {
        id
        name
      }
      """
    )

  product(id: ID!): Product
    @connect(
      http: { GET: "https://myapi.dev/products/{$args.id}" }
      selection: """
      $.result {
        id
        name
        price
        color
      }
      """
      entity: true
    )
}

type Product {
  id: ID!
  name: String
  price: Int
  color: String
}
How does this work?
When the GraphOS Router receives a query for fields from both endpoints, it generates a query plan that sequences calls across the two endpoints. For example:
GraphQL
query ListProductsAndDetails {
  products {
    id
    name
    price
    color
  }
}

Pagination

If your API supports pagination, you can expose that functionality in your schema by defining field arguments and using them in your connector URLs. In addition, you can map pagination metadata like totalCount to fields in your schema.

GraphQL
type Query {
  products(limit: Int = 25, offset: Int): ProductsResult!
    @connect(
      http: {
        GET: "https://myapi.dev/products?limit={$args.limit}&offset={$args.offset}"
      }
      selection: """
      results {
        id
        name
      }
      totalCount: total_count
      """
    )
}

type ProductsResult {
  results: [Product]
  totalCount: Int
}

Adding a "has-many" relationship

You can add fields to types to create relationships between entities in your data model. In this example, the Product type has many reviews. The example uses the Product id field to fetch related reviews.

GraphQL
type Product {
  id: ID!
  reviews: [Review]
    @connect(
      http: { GET: "https://api.example.com/reviews?product_id={$this.id}" }
      selection: """
      $.results {
        id
        rating
      }
      """
    )
}

Creating a bi-directional relationship

You can complete the relationship between two entity types by mapping foreign keys like company_id to primary key fields like Company.id and providing connectors that resolve entities.

GraphQL
type Query {
  user(id: ID!): User
    @connect(
      http: { GET: "https://api.example.com/users/{$args.id}" }
      selection: """
      $.result {
        id
        company: { id: company_id }
      }
      """
      entity: true
    )

  company(id: ID!): Company
    @connect(
      http: { GET: "https://api.example.com/companies/{$args.id}" }
      selection: """
      $.result {
        id
      }
      """
      entity: true
    )
}

type User {
  id: ID!
  company: Company
}

type Company {
  id: ID!
  employees: [User]
    @connect(
      http: { GET: "https://api.example.com/companies/{$this.id}/employees" }
      selection: """
      $.results {
        id
      }
      """
    )
}

Combining multiple representations of the same type

You can add multiple connectors to the same field, and the GraphOS Router chooses to call one or both depending on the fields in the client query. This is especially useful when you have multiple API versions.

GraphQL
type Query {
  product(id: ID!): Product
    @connect(
      http: { GET: "https://myapi.dev/v1/products/{$args.id}" }
      selection: """
      $.result {
        id
        color
      }
      """
      entity: true
    )
    @connect(
      http: { GET: "https://myapi.dev/v2/products/{$args.id}" }
      selection: """
      $.result {
        id
        name
        price
        variants {
          id
          color
        }
      }
      """
      entity: true
    )
}

type Product {
  id: ID!
  name: String
  price: Int
  color: String @deprecated(reason: "Use the 'variants' field instead")
  variants: [ProductVariant]
}

Efficiently fetching additional information

If you provide the GraphOS Router with connectors that fetch additional information, it can choose the optimal endpoint to resolve the requested fields. For example, if the client operation requests the reviews field, the GraphOS Router chooses the second connector to fetch the reviews along with the product details.

GraphQL
type Query {
  product(id: ID!): Product
    @connect(
      http: { GET: "https://myapi.dev/products/{$args.id}" }
      selection: """
      $.result {
        id
        name
        price
      }
      """
      entity: true
    )
    @connect(
      http: { GET: "https://myapi.dev/products/{$args.id}?include=reviews" }
      selection: """
      $.result {
        id
        name
        price
        reviews {
          id
          rating
        }
      }
      """
      entity: true
    )
}

type Product {
  id: ID!
  name: String
  price: Int
  reviews: [Review]
}

Mapping array items to an array of IDs

To transform an array of items into an array of IDs, use the following pattern:

JSON
JSON response with an array of items
1{
2  "id": "order-1",
3  "variantIds": ["27", "11", "347"]
4}
JSON
GraphQL response with array of objects with IDs
1{
2  "data": {
3    "id": "order-1",
4    "items": [
5      {
6        "id": "27"
7      },
8      {
9        "id": "11"
10      },
11      {
12        "id": "347"
13      }
14    ]
15  }
16}
GraphQL
orders.graphql
1type Query {
2  order(id: ID!): Order
3    @connect(
4      source: "api"
5      http: { GET: "/orders/{$args.id}" }
6      # The '@' below refers to the current array item, since
7      # the { id: @ } selection is automatically mapped over
8      # the variantIds array
9      selection: """
10      id
11      items: variantIds { id: @ }
12      """
13    )
14}
15
16type Order @key(fields: "id") {
17  id: ID!
18  items: [ProductVariant!]!
19}
20
21# Product variants will be resolved by the products subgraph
22type ProductVariant @key(fields: "id", resolvable: false) {
23  id: ID!
24}
Feedback

Forums