Apollo Connectors Preview Features
Want to try out the latest and greatest features of Apollo Connectors? You're in the right place!
Before you jump in, there are a couple important pieces of information:
All of these features are subject to change, use care when updating composition or GraphOS Router.
Make sure to tell us what you think before these features are stable!
Enabling Preview Features
Use GraphOS Router
2.2.0-rc.0
or greaterConfigure the router to allow the preview version of connectors:
1connectors:
2 preview_connect_v0_2: true
Use composition
2.11.0-preview.1
or greater withrover
and the "Federation Next" build pipeline in Studio.Update the schema to use the preview version of connectors.
1extend schema
2 @link(
3 url: "https://specs.apollo.dev/connect/v0.2"
4 import: ["@source", "@connect"]
5 )
The order of these steps matters! Older versions of router will not accept the configuration setting, and routers without the configuration will reject schemas composed with the latest composition version.
@connect
on types
You can now apply the @connect
directive to a type!
1type Product
2 @connect(
3 source: "myApi"
4 http: {GET: "/products/{$this.id}"}
5 selection: "id name price"
6 )
7{
8 id: ID!
9 name: String
10 price: String
11}
This works just like an entity: true
field on Query
, but with no field on query!
If you want to add extra fields to a type (via entities), and don't need the root field, this is the way to go.
$batch
for avoiding N+1
If you have a REST endpoint which can accept a list of IDs and return a list of objects, you can use the $batch
variable to avoid the N+1 problem.
$batch
is only available in Connectors that are on types (see above).
Previously, if you had a schema like this:
1type Query {
2 product(id: ID!): Product @connect(
3 source: "myApi"
4 http: {GET: "/product/{$args.id}"}
5 selection: "id name reviews { id }"
6 )
7 review(id: ID!): Review! @connect(
8 source: "myApi"
9 http: {GET: "/reviews/{$args.id}"}
10 selection: "id text rating"
11 entity: true
12 )
13}
14
15type Product {
16 id: ID!
17 name: String
18 price: String
19 reviews: [Review!]!
20}
21
22type Review {
23 id: ID!
24 text: String!
25 rating: Int!
26}
The /reviews
endpoint would be called once for each review ID on the product.
If you have an endpoint that resolves multiple reviews, you can instead do something like this:
1type Query {
2 product(id: ID!): Product @connect(
3 source: "myApi"
4 http: {GET: "/product/{$args.id}"}
5 selection: "id name reviews { id }"
6 )
7}
8
9type Product {
10 id: ID!
11 name: String
12 price: String
13 reviews: [Review!]!
14}
15
16type Review @connect(
17 source: "myApi"
18 http: {
19 POST: "/reviews"
20 body: "ids: $batch.id"
21 }
22 selection: "id text rating"
23) {
24 id: ID!
25 text: String!
26 rating: Int!
27}
$batch
works just like $this
, but it is always an array of the objects being resolved, rather than a single value.
In this example, we use a JSON body to pass the list of IDs to the endpoints, but you can also use queryParams
Limiting batch sizes
When using the $batch
variable, you can limit how many IDs are sent in a single request using batch.maxSize
:
1type Review @connect(
2 source: "myApi"
3 http: {
4 POST: "/reviews"
5 body: "ids: $batch.id"
6 }
7 batch: {maxSize: 5} # If there are more than 5 reviews, they will be split into multiple requests
8 selection: "id text rating"
9) {
10 id: ID!
11 text: String!
12 rating: Int!
13}
More ways to build URLs
More flexible templates
Some restrictions on the templates used in @connect
URLs have been lifted, allowing expressions in a few more places. For example:
1type Query {
2 products(filterName: String, filterValue: String): [Product] @connect(
3 source: "myApi"
4 http: {GET: "/products?{$args.filterName}={$args.filterValue}"}
5 selection: "id name reviews { id }"
6 )
7}
Setting the query parameter name was not previously allowed, only the value.
Dynamic expressions will still always be percent-encoded and are still not allowed to modify the domain of the URL.
Adding multiple path parameters dynamically
The new http.path
variable is available in both @source
and @connect
, and allows appending multiple path parameters to the URL.
It uses the same mapping expressions as other parts of Connectors.
1extend schema
2 @source(name: "myApi", http: {
3 baseURL: "http://example.com",
4 path: "$config.pathComponents"
5 })
6
7type Query {
8 products(pathComponents: [String!]!): [Product] @connect(
9 source: "myApi"
10 http: {GET: "/products", path: "$args.pathComponents"}
11 selection: "id name reviews { id }"
12 )
13}
Path components will be appended starting with @source(http.baseURL)
, then @source(http.path)
, then the @connect
template, and finally @connect(http.path)
.
Expressions in http.path
must evaluate to arrays of scalars. Each value in that array will be percent-encoded and appended with a new slash.
The example above might result in a URL like http://example.com/from/config/products/from/arguments
.
Adding multiple query parameters dynamically
The new http.queryParams
attribute works much like http.path
, but must evaluate to an object where each key is a query parameter name and each value is
a query parameter value or list of values. Query parameters are appended (not overridden) in the same order as path segments:
Literal parameters in
baseURL
Dynamic parameters (for example, from
$config
) in@source(http.queryParams)
Query parameters from the connect template (static or from
{ }
expressions)Dynamic parameters in
@connect(http.queryParams)
The last of those is especially important for batching:
1type Review @connect(
2 source: "myApi"
3 http: {
4 GET: "/reviews"
5 queryParams: "id: $batch.id"
6 }
7 selection: "id text rating"
8) {
9 id: ID!
10 text: String!
11 rating: Int!
12}
Because $batch.id
is an array of IDs, the resulting URL will have multiple id
query parameters, like http://example.com/reviews?id=1&id=2&id=3
.