Common Patterns for Apollo 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
)
}
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}