Troubleshooting Connectors

Fixes and workarounds for general issues and specific errors


Preview
Apollo Connectors are currently in public preview. To get started, you need an Apollo account with a GraphOS Trial or Enterprise plan.
💡 tip
During the preview, make sure you're using the latest versions of both Federation composition (2.10.0-preview.0) and GraphOS Router (2.0.0-preview.0).

Return debug info in GraphQL responses

To diagnose issues with your connectors, you can return debugging information as part of each GraphQL response, including the details of each HTTP request and response from your connectors.

⚠️ caution
Because this feature can leak sensitive information, enable it only for local development environments.

First, run your router in development mode. This happens automatically when using rover dev. If you're running a router manually, you can pass it the --dev CLI argument.

Apollo Sandbox presents request, response, and mapping information in a panel after you execute an operation.

Connectors debugger screenshot

Satisfiability errors

If you see a composition error about "satisfiability", you've run into one of the most important concepts when composing various REST endpoints into a coherent graph.

 note
The rules of Federation composition, including rules for unreachable fields, apply equally to connectors and GraphQL subgraphs.

Consider a schema with two connectors that hit different endpoints and get differing representations of a type. The goal is to merge the representations into a unified type so that clients can access all the fields without caring about the underlying data sources.

GraphQL
1type Query {
2  posts: [Post]
3    @connect(
4      http: { GET: "https://api.example.com/v1/posts" }
5      selection: "id title createdAt"
6    )
7
8  post(id: ID!): Post
9    @connect(
10      http: { GET: "https://api.example.com/v2/posts/{$args.id}" }
11      selection: "id title body"
12    )
13}
14
15type Post {
16  id: ID!
17  title: String
18  body: String
19  createdAt: String
20}

These connectors don't form a coherent graph. We can't reach body from the Query.posts field, and we can't reach createdAt from the Query.post(id:) field. The composition error looks like this:

Text
1SATISFIABILITY_ERROR: The following supergraph API query:
2{
3  posts {
4    body
5  }
6}
7cannot be satisfied by the subgraphs because:
8- from subgraph "posts":
9  - cannot find field "Post.body".
10  - cannot move to subgraph "posts", which has field "Post.body", because type "Post" has no @key defined in subgraph "posts".
11SATISFIABILITY_ERROR: The following supergraph API query:
12{
13  post(id: "<any id>") {
14    createdAt
15  }
16}
17cannot be satisfied by the subgraphs because:
18- from subgraph "posts":
19  - cannot find field "Post.createdAt".
20  - cannot move to subgraph "posts", which has field "Post.createdAt", because type "Post" has no @key defined in subgraph "posts".
 note
During the preview, the error messages for satisfiability suggest fixes that apply to GraphQL subgraphs, not connectors. Namely, adding a @key directive and an entity resolver for the type.For a connector, adding entity: true is the equivalent of defining a @key and an entity resolver.

The first issue (body) is easily solved with entity connectors. By adding entity: true to the connector on Query.post(id:), the query planner can make subsequent fetches to fetch that field.

GraphQL
1type Query {
2  post(id: ID!): Post
3    @connect(
4      http: { GET: "https://api.example.com/v2/posts/{$args.id}" }
5      selection: "id title body"
6      entity: true
7    )
8}

The second issue (createdAt) doesn't have a straightforward solution. We need an endpoint that takes the ID of a Post and returns the createdAt field. The simplest solution is to add a second connector to the Query.post(id:) field:

GraphQL
1type Query {
2  post(id: ID!): Post
3    @connect(
4      http: { GET: "https://api.example.com/v2/posts/{$args.id}" }
5      selection: "id title body"
6      entity: true
7    )
8    @connect(
9      http: { GET: "https://api.example.com/v1/posts/{$args.id}" }
10      selection: "id createdAt"
11      entity: true
12    )
13}

Now there is always a way to resolve all fields, regardless of which Query root field the client uses.

💡 tip
Read more about the rules for entity: true.

Circular references

If you get a composition error about circular references, you've run into a connectors preview limitation. Direct circular references, such as User.friends: [User], are not supported. Indirect circular references, such as User.company: Business and Business.employees: [User], are supported using certain patterns.

More details
When using a connector, a selection of a type can't refer to itself. For example, this is invalid:
GraphQL
1type Query {
2  user(id: ID!): User
3    @connect(
4      http: { GET: "https://api.example.com/users/{$args.id}" }
5      selection: """
6      id
7      name
8      friends { # Circular reference to User
9        id
10      }
11      """
12    )
13}
14
15type User {
16  id: ID!
17  name: String
18  friends: [User]
19}
This occurs if any type within the selection can lead to a circular reference, not just the top-level type. For example, this is also invalid:
GraphQL
1type Query {
2  user(id: ID!): User
3    @connect(
4      http: { GET: "https://api.example.com/users/{$args.id}" }
5      selection: """
6      id
7      name
8      favoriteBooks { # First reference to Book
9        id
10        author {
11          id
12          books { # Circular reference to Book
13            id
14          }
15        }
16      }
17      """
18    )
19}
20
21type User {
22  id: ID!
23  name: String
24  favoriteBooks: [Book]
25}
26
27type Book {
28  id: ID!
29  author: Author
30}
31
32type Author {
33  id: ID!
34  books: [Book]
35}
To solve this problem, use another connector:
GraphQL
1type Query {
2  user(id: ID!): User
3    @connect(
4      http: { GET: "https://api.example.com/users/{$args.id}" }
5      selection: """
6      id
7      name
8      favoriteBooks {
9        id
10        title
11        author {
12          id
13          # No reference to books here
14        }
15      }
16      """
17    )
18}
19
20type User {
21  id: ID!
22  name: String
23  favoriteBooks: [Book]
24}
25
26type Book {
27  id: ID!
28  title: String!
29  author: Author
30    @connect(
31      http: { GET: "https://api.example.com/books/{$this.id}/author" }
32      selection: """
33      id
34      name
35      # No reference to books
36      """
37    )
38}
39
40type Author {
41  id: ID!
42  books: [Book]
43    @connect(
44      http: { GET: "https://api.example.com/authors/{$this.id}/books" }
45      selection: """
46      id
47      title
48      # No reference to author
49      """
50    )
51}
Feedback

Forums