Common Patterns for Connectors
Examples of how to use connectors
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.
type Query {
products: [Product]
@connect(
http: { GET: "https://myapi.dev/products" }
selection: """
$.results {
id
name
price
}
"""
)
}
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.
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?
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.
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.
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.
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.
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.
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:
1{
2 "id": "order-1",
3 "variantIds": ["27", "11", "347"]
4}
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}
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.
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:
An
apollo.config.yaml
file in the root of your project containingrover: {}
A
supergraph.yaml
file also in the root of your project, which is the configuration file used forrover dev
,rover supergraph compose
, and this VS Code extension.Make sure to set the composition version to 2.10.0-preview.0.
Make sure every file you want feedback on is included in the
subgraphs
section.
supergraph.yaml
by setting the rover.supergraphConfig
option in
apollo.config.yaml
, like this: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:
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.