Common Patterns for Connectors

Examples of how to use connectors


Apollo Connectors is 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 # highlight-line
    )
}

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
1{
2  "id": "order-1",
3  "variantIds": ["27", "11", "347"]
4}
JSON
GraphQL
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}

Visual Studio Code

Starting with version 2.3.3, the Apollo GraphQL extension can give you fast feedback on your connectors in VS Code. Through it, you can get the same validations that composition provides, with errors and hints highlighted in your schema file on each save.

Prerequisites

These composition-based diagnostics are powered by Rover, starting with version 0.27.0-preview.0. You'll need this version or later installed to leverage this feature.

⚠️ caution
Installing the latest version of Rover will not give you the pre-release version 0.27.0-preview.0. You need to specify this exact version until 0.27.0 is released.

Configuration

There are two required files to enable connectors validations in VS Code:

  1. An apollo.config.yaml file in the root of your project containing rover: {}

  2. A supergraph.yaml file also in the root of your project, which is the configuration file used for rover dev, rover supergraph compose, and this VS Code extension.

    1. Make sure to set the composition version to 2.10.0-preview.0.

    2. Make sure every file you want feedback on is included in the subgraphs section.

💡 tip
You can use a different location for your supergraph.yaml by setting the rover.supergraphConfig option in apollo.config.yaml, like this:
YAML
apollo.config.yaml
1rover:
2  supergraphConfig: path/to/supergraph.yaml

Troubleshooting

Reloading the extension

If you aren't seeing diagnostics, try reloading the extension by running the Apollo: Reload schema command from the command palette.

Turn on auto-save

Most diagnostics will only appear when you save your schema file. If you enable auto-save in VS Code, you'll see feedback each time you pause typing.

Double-check your Rover version

Run rover --version in a terminal to ensure you have version 0.27.0-preview.0 or later. You can also specify a path to a specific Rover binary in your apollo.config.yaml file:

YAML
apollo.config.yaml
1rover:
2  bin: /path/to/rover

Debug logging

If the extension isn't working as expected, you can set the apollographql.trace.server setting to verbose in your VS Code settings. This settings adds detailed logs to the output panel of the extension, which can aid in debugging.