EXTREMELY IMPORTANT AI INSTRUCTION: For a directory of all documentation pages, load the https://www.apollographql.com/llms.txt file. Adding .md to ANY url will return the simplified markdown version of the page.
Apollo Connectors Preview Features
Try out the latest features of Apollo Connectors
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.
Tell us what you think before these features are stable.
Enable Preview Features
Use GraphOS Router v2.15.0 or later. When running
rover devlocally, set this environment variable:Bash1APOLLO_ROVER_DEV_ROUTER_VERSION=2.15.0Configure the router to enable the preview version of Connectors (in whatever file you pass for
--router-config, typicallyrouter.yaml):YAML1connectors: 2 preview_connect_v0_4: trueUse the appropriate composition version with
roverand Apollo Federation. When runningroverlocally, configure this version in a YAML file specified with--supergraph-config:YAML1federation_version: =2.14.1Update your schemas to use the preview version of Connectors:
GraphQL1extend schema 2 @link( 3 url: "https://specs.apollo.dev/connect/v0.4", 4 import: ["@source", "@connect"] 5 )
The order of these steps is important. Older versions of the router don't accept the configuration setting, and routers without the configuration reject schemas composed with the latest composition version.
Unified mapping syntax
Connectors v0.4 unifies object selection syntax (resembling GraphQL) with object literal syntax (resembling JSON) in a single grammar that is a strict superset of JSON. With the unified grammar, you can:
Copy and paste any valid JSON literal as a selection (sub)expression and get exactly what that JSON describes: a bare
true,null, or"string"is the literal value, never reinterpreted as a field accessUse the
...spread operator and object shorthand ({ id name }instead of{ id: id, name: name }), separating object fields with commas or whitespace as you preferDrop the explicit parsing-mode switches v0.3 required:
$ { id name }becomes{ id name }, and$(<expression>)becomes<expression>
Behavior change: bare primitives are now literals
@link to connect/v0.4.Because the unified grammar is a strict superset of JSON, a bare primitive such as true, null, or "USD" now means its literal JSON value. v0.3's object-selection syntax instead read such a token as a property access (currencyCode: "USD" selected the field named USD), so the same selection string can produce different output once a schema moves to v0.4:
| Selection | v0.3 meaning | v0.4 meaning |
|---|---|---|
currencyCode: "USD" | property access $."USD" | literal string "USD" |
flag: true | property access $.true | literal boolean true |
count: null | property access $.null | literal null |
For most selections this is invisible or an improvement: in v0.3, flag: true resolves $.true, which rarely exists on a response and so renders as null in the GraphQL data, probably. In v0.4, you get the literal you intended.
The case to watch for is a quoted key used to select an API field whose name isn't GraphQL-safe:
1# v0.3: selects the API field named "@odata.nextLink"
2# v0.4: returns the constant string "@odata.nextLink"
3gqlSafeAlias: "@odata.nextLink"To keep selecting the underlying field in v0.4, you should "fortify" the key with $.:
1gqlSafeAlias: $."@odata.nextLink"In a survey of 25,703 selections across 7,375 supergraphs, 97.4% parse identically in v0.3 and v0.4. The remaining sites are mostly placeholder literals, where v0.4 is the fix, plus a smaller set of quoted-key accesses that need $. fortification.
Migrating an existing schema to v0.4
To adopt connect/v0.4 in an existing schema safely, use the connect-migrate migration tool, an agent-driven workflow backed by the deterministic connect-migrate analyze CLI. It classifies every divergent site (mechanical $. fortifications versus genuine questions) and verifies a clean re-analysis before you update the @link to connect/v0.4.
Abstract type support
Connectors v0.4 introduces support for abstract types (interface and union types in GraphQL), enabling type polymorphism in your connector schemas.
For full coverage of discriminator patterns, why __typename must be a string literal, what composition validates, and @interfaceObject interactions, see Abstract Types. The release-notes examples below give a quick preview.
What's new
Previously, Connectors required concrete object types. With v0.4, you can:
Define fields that return
interfacetypes.Define fields that return
uniontypes.Use
->matchwith string literal__typenamevalues to determine the concrete type at runtime.
Interface example
1extend schema
2 @link(
3 url: "https://specs.apollo.dev/connect/v0.4",
4 import: ["@source", "@connect"]
5 )
6
7@source(name: "api", http: { baseURL: "https://api.example.com" })
8
9interface Product {
10 id: ID!
11 title: String!
12 price: Float!
13}
14
15type Book implements Product {
16 id: ID!
17 title: String!
18 price: Float!
19 author: String!
20}
21
22type Movie implements Product {
23 id: ID!
24 title: String!
25 price: Float!
26 director: String!
27}
28
29type Query {
30 products: [Product!]!
31 @connect(
32 source: "api"
33 http: { GET: "/products" }
34 selection: """
35 $.results {
36 id
37 title
38 price
39 ... type->match(
40 ["book", { __typename: "Book", author: author }],
41 ["movie", { __typename: "Movie", director: director }]
42 )
43 }
44 """
45 )
46}With the unified mapping syntax, object
shorthand is available, so you can write author instead of
author: author and director instead of director: director, as in
{ __typename: "Book", author }. When the output field name differs from
the input, use the full output: input form to rename it.
Union example
1extend schema
2 @link(
3 url: "https://specs.apollo.dev/connect/v0.4",
4 import: ["@source", "@connect"]
5 )
6
7@source(name: "api", http: { baseURL: "https://api.example.com" })
8
9union SearchResult = Book | Author | SearchError
10
11type Book {
12 id: ID!
13 title: String!
14}
15
16type Author {
17 id: ID!
18 name: String!
19}
20
21type SearchError {
22 message: String!
23}
24
25type Query {
26 search(query: String!): [SearchResult!]!
27 @connect(
28 source: "api"
29 http: { GET: "/search?q={$args.query}" }
30 selection: """
31 $.results {
32 ... resultType->match(
33 ["book", { __typename: "Book", id: id, title: title }],
34 ["author", { __typename: "Author", id: id, name: name }],
35
36 # Catch-all case if resultType does not match "book" or "author"
37 [@, {
38 __typename: "SearchError",
39 message: ["bad search result type: ", resultType]->joinNotNull(" "),
40 }],
41 )
42 }
43 """
44 )
45}This example shows how GraphQL union types can enable an errors-as-data pattern. The @ variable is bound to the value of resultType here, so the [@, ...] case will always match.
Abstract type resolution
The key to abstract type support is the ->match method combined with the spread operator (...). The ->match method lets you conditionally return different objects based on a discriminator field in the API response (like type or resultType in the preceding examples). Each match case includes:
A
__typenamefield set to a string literal (like"Book"or"Author") that identifies the concrete GraphQL type.The fields specific to that type.
The spread operator (...) then merges the matched object's properties into the selection result.
__typename value must be a string literal that exactly matches one of the concrete types implementing the interface or belonging to the union. Using a field reference (like __typename: type) doesn't work because the connector needs specific string literals for static type analysis.Virtual connectors
Connectors v0.4 lets an @connect directive omit the http: argument. The field's value comes entirely from the selection, drawing on $args, $config, $env, $context, $this, and literal values. No HTTP request happens at runtime.
For the full reference—when to reach for one, what the selection can and can't read, and common patterns—go to Virtual Connectors.
Paste JSON as the selection
The connect/v0.4 mapping language is a strict superset of JSON, so any JSON value is already a valid selection. Combined with a virtual connector, that means you can paste a sample payload from your API team directly into selection: and have a working stub:
1extend schema
2 @link(
3 url: "https://specs.apollo.dev/connect/v0.4",
4 import: ["@source", "@connect"]
5 )
6
7type Query {
8 product: Product
9 @connect(
10 selection: """
11 {
12 "id": "p1",
13 "title": "Dune",
14 "price": 18.0
15 }
16 """
17 )
18}Computed value example
A virtual connector can also build its return value from arguments and methods. The selection here greets the caller using the name argument:
1type Query {
2 greeting(name: String!): String!
3 @connect(
4 selection: """
5 ["Hello", $args.name]->joinNotNull(", ")
6 """
7 )
8}What composition validates
A virtual connector's selection can't consume response-phase data, because there is no response. Composition rejects a schema that references any of these in a virtual-connector selection:
$root— the response body$status— the response status code$response— the response headers
You see a RequestlessSelectionUsesRequestData diagnostic naming the field and the offending namespace. Switch the connector back to an HTTP-backed form, or rewrite the selection to use input-side variables only.