Get Started with Apollo Connectors and GraphOS

Connect and orchestrate REST APIs with a unified GraphQL API


Apollo Connectors are the quickest way to set up a GraphQL API backed by REST endpoints.




In this guide, you will:

  • Set up a GraphQL API

  • Connect live REST API endpoints to your GraphQL API using Apollo Connectors

  • Make requests in a sandbox environment to validate your GraphQL API is orchestrating REST API calls

  • Learn how to use Apollo's tools like GraphOS Studio, Rover CLI and Apollo Sandbox

Prefer to start with a high-level conceptual understanding? Check out the Apollo Connectors Overview. Otherwise, let's start building!

Step 1. Set up your GraphQL API

This step sets up a GraphQL API in a development environment using the following Apollo tools:

  • GraphOS Studio: This is the primary web interface for GraphOS. Studio helps you monitor, manage, and collaborate on your GraphQL APIs.

  • The Rover CLI: This is the primary command-line interface for GraphOS.

  • Apollo Sandbox: This local development tool lets you test requests, inspect responses, debug connectors, and more.

Create a GraphOS account

note
Skip to the next section if you already have a GraphOS account.

You need an account to work with GraphOS. Sign up for a Free plan at studio.apollographql.com.

Create a graph

A GraphQL API exposes a graph, a schema-defined network of interconnected data that enables clients to traverse relationships and fetch exactly what they need in a single request. To set up your GraphQL API, start by creating a new graph in GraphOS Studio.

  1. If your organization doesn't have any graphs yet, click Connect a REST API.

    Click the Connect a REST API button
  2. If you already have existing graphs, click the down chevron () to the right of Create a new graph, then click Connect a REST API.

  3. Enter a graph title and graph ID and keep the defaults for the other form elements. Then click Create and continue.

    A form titled 'Connect a REST API'.
The 'Graph title' field has the value 'Ecomm Example'.
The 'Graph ID' field has the value 'Ecomm-Example'.
There is a button which says 'Create and continue'.
  4. Next, follow the instructions to set up your local development environment. You should end up with a supergraph.yaml and products.graphql file in your local project.

    File overview
    • The supergraph.yaml specifies which Apollo Federation version to use and configures your subgraphs, the different services that make up your supergraph.
    • The products.graphql contains a GraphQL schema and defines your graph's structure, types, and capabilities. The schema is where you define connectors and where this guide spends most of its time.

    Once rover dev is running, your terminal should look something like this:

    terminal
    supergraph config loaded successfully
    warning: Do not run this command in production! It is intended for local development only.
    ==> Watching products.graphql for changes
    WARN: Connector debugging is enabled, this may expose sensitive information.
    ==> your supergraph is running! head to http://localhost:4000 to query your supergraph
    successfully composed with version 2.10.0

    The last step in the modal brings you back to this quickstart guide.

🎉 Congratulations—you've successfully started a local development session where you can explore, develop, and publish updates to your graph!

Step 2. Connect REST APIs

In this step, you will:

  • Walk through an example GraphQL schema to learn how connectors declaratively integrate REST APIs.

  • Make requests in Apollo Sandbox to observe how connectors orchestrate calls to the connected REST APIs.

  • Create more connectors from scratch, where each connector demonstrates a different capability of Apollo Connectors.

Love developer tooling? 🛠️
Whether you're developing in VS Code, Vim/NeoVim, or with a JetBrains IDE, Apollo's got you covered with tooling for a better developer experience. Now is a great time to set up these plugins to get better autocomplete and faster feedback as you develop.

Explore example connector

Let's take a look at the products.graphql GraphQL schema to see how connectors work.

  1. The first few lines use @link directives to enable the latest versions of federation and connectors.

    GraphQL
    products.graphql
    extend schema
      @link(
        url: "https://specs.apollo.dev/federation/v2.10"
        import: ["@key"]
      )
      @link(
        url: "https://specs.apollo.dev/connect/v0.1"
        import: ["@connect", "@source"]
      )
  2. In the next few lines, the @source directive creates a reusable configuration that each connector can reference. In this case, it only sets the baseURL to the ecommerce demo API this guide uses. You can also add request headers to a @source.

    GraphQL
    products.graphql
      @source(
        name: "ecomm"
        http: { baseURL: "https://ecommerce.demo-api.apollo.dev/" }
      )
  3. Product is the standard object type the GraphQL API returns.

    GraphQL
    products.graphql
    type Product {
      id: ID!
      name: String
      description: String
    }
  4. The Query type has a products field that the @connect directive implements.

    What's the Query type?
    In a GraphQL schema, the Query type defines the entry points for reading data from a GraphQL schema. It acts as the root type that clients use to request specific fields and traverse the graph.
    GraphQL
    products.graphql
    type Query {
      products: [Product]
        # A @connect directive defines the API data source of a GraphQL schema field.
        @connect(
          source: "ecomm"
          http: { GET: "/products" }
          selection: """
          $.products {  
            id
            name
            description
          }
          """
        )
    }

    Here is what each of @connect's arguments does:

    • source: "ecomm" refers to the @source instance.

    • http sets the method (GET) and path (/products) for the request.

    • selection maps the REST API's JSON response to the fields in the GraphQL schema.

To understand how to map a JSON response to your GraphQL schema, you inspect the JSON response. Here's what the response for /products looks like:

/products
JSON
1{
2  "products": [
3    {
4      "id": 1,
5      "name": "Lunar Rover Wheels",
6      "createdAt": 1636742972000,
7      "updatedAt": 1636742972000,
8      "description": "Designed for traversing the rugged terrain of the moon, these wheels provide unrivaled traction and durability. Made from a lightweight composite, they ensure your rover is agile in challenging conditions.",
9      "slug": "lunar-rover-wheels",
10      "tags": [
11        {
12          "tagId": "space",
13          "name": "Space"
14        },
15        {
16          "tagId": "engineering",
17          "name": "Engineering"
18        },
19        {
20          "tagId": "rover",
21          "name": "Rover"
22        }
23      ],
24      "category": "Engineering Components",
25      "availability": "AVAILABLE"
26    },
27    {
28      "id": 2,
29      "name": "Zero-Gravity Moon Boots",
30      "createdAt": 1636742972000,
31      "updatedAt": 1636742972000,
32      "description": "Experience weightlessness with our Zero-Gravity Moon Boots! Specifically designed to provide comfort and support for lunar explorers, these boots are perfect for hopping around on the moon's surface.",
33      "slug": "zero-gravity-moon-boots",
34      "tags": [
35        {
36          "tagId": "space",
37          "name": "Space"
38        },
39        {
40          "tagId": "apparel",
41          "name": "Apparel"
42        },
43        {
44          "tagId": "moon",
45          "name": "Moon"
46        }
47      ],
48      "category": "Apparel",
49      "availability": "AVAILABLE"
50    },
51    {
52      "id": 3,
53      "name": "Asteroid Blaster Tool",
54      "createdAt": 1636742972000,
55      "updatedAt": 1636742972000,
56      "description": "A must-have tool for all space engineers! This high-powered blaster is designed for asteroid excavation and resource gathering, featuring an adjustable power setting for all your blasting needs.",
57      "slug": "asteroid-blaster-tool",
58      "tags": [
59        {
60          "tagId": "engineering",
61          "name": "Engineering"
62        },
63        {
64          "tagId": "tools",
65          "name": "Tools"
66        },
67        {
68          "tagId": "space",
69          "name": "Space"
70        }
71      ],
72      "category": "Engineering Tools",
73      "availability": "AVAILABLE"
74    },
75    {
76      "id": 4,
77      "name": "Interstellar Communication Device",
78      "createdAt": 1636742972000,
79      "updatedAt": 1636742972000,
80      "description": "Stay connected across the galaxies! Our Interstellar Communication Device allows you to send messages to fellow space travelers with secure encryption and planetary signal enhancement.",
81      "slug": "interstellar-communication-device",
82      "tags": [
83        {
84          "tagId": "tech",
85          "name": "Tech"
86        },
87        {
88          "tagId": "space",
89          "name": "Space"
90        },
91        {
92          "tagId": "communication",
93          "name": "Communication"
94        }
95      ],
96      "category": "Communication Devices",
97      "availability": "AVAILABLE"
98    },
99    {
100      "id": 5,
101      "name": "Galactic Navigation System",
102      "createdAt": 1636742972000,
103      "updatedAt": 1636742972000,
104      "description": "Never get lost in space again! Our Galactic Navigation System provides real-time location tracking, route planning, and hazard warnings to ensure safe travels through the cosmos.",
105      "slug": "galactic-navigation-system",
106      "tags": [
107        {
108          "tagId": "tech",
109          "name": "Tech"
110        },
111        {
112          "tagId": "navigation",
113          "name": "Navigation"
114        },
115        {
116          "tagId": "space",
117          "name": "Space"
118        }
119      ],
120      "category": "Navigation Devices",
121      "availability": "AVAILABLE"
122    },
123    {
124      "id": 6,
125      "name": "Mars Terrain Analyzer",
126      "createdAt": 1636742972000,
127      "updatedAt": 1636742972000,
128      "description": "A state-of-the-art device crafted for analyzing the soil and terrain of Mars. It provides scientists with crucial data about soil composition, moisture levels, and potential resources for future colonization.",
129      "slug": "mars-terrain-analyzer",
130      "tags": [
131        {
132          "tagId": "science",
133          "name": "Science"
134        },
135        {
136          "tagId": "analysis",
137          "name": "Analysis"
138        },
139        {
140          "tagId": "mars",
141          "name": "Mars"
142        }
143      ],
144      "category": "Scientific Devices",
145      "availability": "AVAILABLE"
146    },
147    {
148      "id": 7,
149      "name": "Comet Dust Collector",
150      "createdAt": 1636742972000,
151      "updatedAt": 1636742972000,
152      "description": "Gather dust from comets with this innovative collector! Designed for deep-space research, it captures samples with high efficiency while ensuring the integrity of the materials.",
153      "slug": "comet-dust-collector",
154      "tags": [
155        {
156          "tagId": "science",
157          "name": "Science"
158        },
159        {
160          "tagId": "research",
161          "name": "Research"
162        },
163        {
164          "tagId": "space",
165          "name": "Space"
166        }
167      ],
168      "category": "Research Equipment",
169      "availability": "AVAILABLE"
170    },
171    {
172      "id": 8,
173      "name": "Planetary Habitat Module",
174      "createdAt": 1636742972000,
175      "updatedAt": 1636742972000,
176      "description": "An essential for any space colonization mission! This habitat module provides a reliable, comfortable living environment for astronauts on planetary missions, complete with life support systems and smart technology.",
177      "slug": "planetary-habitat-module",
178      "tags": [
179        {
180          "tagId": "construction",
181          "name": "Construction"
182        },
183        {
184          "tagId": "space",
185          "name": "Space"
186        },
187        {
188          "tagId": "habitat",
189          "name": "Habitat"
190        }
191      ],
192      "category": "Living Spaces",
193      "availability": "AVAILABLE"
194    },
195    {
196      "id": 9,
197      "name": "Satellite Launch Pad Kit",
198      "createdAt": 1636742972000,
199      "updatedAt": 1636742972000,
200      "description": "Get your small satellites off the ground with our Satellite Launch Pad Kit! This kit includes everything you need to successfully launch mini satellites into orbit, including launch software and tracking system.",
201      "slug": "satellite-launch-pad-kit",
202      "tags": [
203        {
204          "tagId": "space",
205          "name": "Space"
206        },
207        {
208          "tagId": "launch",
209          "name": "Launch"
210        },
211        {
212          "tagId": "kit",
213          "name": "Kit"
214        }
215      ],
216      "category": "Launch Equipment",
217      "availability": "AVAILABLE"
218    },
219    {
220      "id": 10,
221      "name": "Planetary Resource Extractor",
222      "createdAt": 1636742972000,
223      "updatedAt": 1636842972000,
224      "description": "Tap into the resources of asteroids and moons! The Planetary Resource Extractor is engineered for durability and efficiency to help miners gather valuable materials from celestial bodies.",
225      "slug": "planetary-resource-extractor",
226      "tags": [
227        {
228          "tagId": "engineering",
229          "name": "Engineering"
230        },
231        {
232          "tagId": "mining",
233          "name": "Mining"
234        },
235        {
236          "tagId": "space",
237          "name": "Space"
238        }
239      ],
240      "category": "Mining Equipment",
241      "availability": "AVAILABLE"
242    }
243  ],
244  "summary": {
245    "total": 30
246  }
247}

Notice that the /products endpoint returns many more fields than id, name, and description. With connectors (and for the sake of simplicity in this guide), you can select and return only the ones you need via selection mapping.

Selection mapping

In selection mapping, $ refers to the root of the response body. Since the list of products is nested under a products key, the first part of the selection is $.products. The product fields to map are wrapped within the $.products {}object. Since the Product type fields declared in the GraphQL schema map cleanly to product fields in the JSON response, the selection uses shorthand for field names.

These two selection mappings are equivalent:

GraphQL
selection: """
$.products {  
  id
  name
  description
}
GraphQL
selection: """
$.products {  
  id: id
  name: name
  description: description
}
What if schema field names are different from the JSON response?
You can use selection mapping to rename fields in your API. For example, if your REST API returns a name field that you want to rename title in your GraphQL API, you can map it like so:
GraphQL
selection: """
$.products {  
  id,
  title: name
  description
}

Notice you can still use shorthand for any other field names that don't need to change.
note
You can use selection mapping to transform values beyond just renaming fields.

Let's test things out by running a request in the Apollo Sandbox.

Run a request

Running rover dev starts a local GraphQL server at http://localhost:4000. This is an instance of Apollo Sandbox. You can write operations and make a request to your GraphQL API in Apollo Sandbox, then use its tools to validate the response and debug issues.

  1. In a browser, go to http://localhost:4000.

  2. In the central Operation panel, copy and paste the following query to get all products.

    GraphQL
    Example query
    query Products {
      products {
        id
        name
        description
      }
    }
  3. Run the request by clicking the ▶️ Products button. Check the response.

    You should see the products data you selected from the /products endpoint.

    Viewing requests in the Connectors Debuggers in Apollo Sandbox

That's it—a fully functional GraphQL API built on connectors! You'll create a second connector from scratch. Before that, this guide offers a slight detour to showcase the Sandbox's Connectors Debugger. If you're eager to write your own connector, can skip to the next section and return here when you need it.

Explore connectors debugger

Explore the connectors debugger

Suppose the example connector hadn't worked out of the box—for example, that the selection mapping was incorrect. You can use the Connectors Debugger in the Sandbox to figure out what's gone wrong.
  1. Update your products.graphql so that the selection mapping is incorrect in the following way:
    GraphQL
    products.graphql
    type Query {
      products: [Product]
        # A @connect directive defines the API data source of a GraphQL schema field.
        @connect(
          source: "ecomm"
          http: { GET: "/products" }
          selection: """
          id
          name
          description
          """
        )
    }
    Why is this incorrect?
    This selection mapping doesn't wrap the field names in $.products{ } and therefore doesn't match the JSON response from /products. It would match if that endpoint returned a top-level list of products that weren't nested under a products key.
  2. Save your products.graphql to hot reload the Sandbox.
  3. Rerun the Products query by clicking the ▶️ Products button. Check the response.
    GraphQL
    Example query
    query Products {
      products {
        id
        name
        description
      }
    }
    With the incorrect schema, the Response panel shows null for the data returned.Query products' info with connector subgraphThe Response panel also has a Connectors Debugger that can help determine what's gone wrong.
  4. Open it by clicking Response and selecting Connectors Debugger from the dropdown.Opening the Connectors Debugger from Apollo Sandbox
  5. You can see three mapping errors occurred, even though the HTTP response status code was 200.Viewing requests in the Connectors Debuggers in Apollo Sandbox
  6. Clicking into the Mapping panel displays some helpful error messages: Property .description not found in object, etc.
  7. The Response body panel of the debugger shows the raw JSON response and where the schema went wrong. The /products endpoint doesn't just return a list, it returns a list of products nested under the products key.Viewing requests in the Connectors Debuggers in Apollo Sandbox
  8. Correct the selection mapping in products.graphql by reverting to the original schema, nesting the current selection inside of $.products{ }.
    GraphQL
    products.graphql
    type Query {
      products: [Product]
        # A @connect directive defines the API data source of a GraphQL schema field.
        @connect(
          source: "ecomm"
          http: { GET: "/products" }
          selection: """
          $.products {
            id
            name
            description
          }
          """
        )
    }
  9. Save your changes to the products.graphql file in your editor.
  10. Rerun your request by clicking the ▶️ Products button. The Connectors Debugger should now show no errors. Toggle the right panel to the Response to confirm the response is as expected.Viewing requests in the Connectors Debuggers in Apollo Sandbox

Create a connector with arguments

Next, you'll create your own connector to interact with the /products/:id endpoint. Adding this connector to your schema lets clients request a single product by ID from the same GraphQL API that lists all products.

  1. Add a new product field to the root Query type that requires an argument called id and returns a Product.

    GraphQL
    products.graphql
    type Query {
      products: [Product]
        @connect(
          source: "ecomm"
          http: { GET: "/products" }
          selection: """
          $.products {
            id
            name
            description
          }
          """
        )
    
      product(id: ID!): Product
    }
    GraphQL argument syntax
    In a GraphQL schema, arguments are enclosed in parentheses that come after a field's name. Each argument consists of two parts: 1) the argument's name and 2) the type of data that will be passed as the argument's value. Learn more about GraphQL arguments.
  2. Next, use the @connect directive to connect Query.product to the /products/:id endpoint. You can use arguments as path and query parameters via the $args variable.

    GraphQL
    products.graphql
    type Query {
      products: [Product]
        @connect(# -- snip -- #)
    
      product(id: ID!): Product
        @connect(
          source: "ecomm"
          http: { GET: "/products/{$args.id}" }
        )
    }
  3. Inspect the response body for this type of request to your REST API, for example, /products/1.

    /products/1
    JSON
    {
      "id": 1,
      "name": "Lunar Rover Wheels",
      "createdAt": 1675200000000,
      "updatedAt": 1675200000000,
      "description": "Innovatively designed wheels for lunar rovers, built to endure harsh moon terrain and provide optimal agility. Each wheel is constructed using advanced materials to withstand temperature fluctuations and dust.",
      "slug": "lunar-rover-wheels",
      "tags": [
        {
          "tagId": "1",
          "name": "Instruments"
        },
        {
          "tagId": "2",
          "name": "Space"
        }
      ],
      "category": "Engineering Components",
      "availability": "AVAILABLE",
      "variants": [
        {
          "name": "Standard Wheel",
          "price": {
            "original": 4999,
            "discounts": [],
            "final": 4999
          },
          "specifications": {
            "Material": {
              "value": "Titanium alloy"
            },
            "Diameter": {
              "value": "50 cm"
            }
          },
          "inventory": {
            "quantity": 100,
            "sellUnavailable": false
          },
          "shipping": {
            "ship1": {
              "weight": 5,
              "method": "GROUND",
              "estimate": {
                "price": 499,
                "arrival": 1675804800000
              }
            },
            "ship2": {
              "weight": 5,
              "method": "AIR",
              "estimate": {
                "price": 999,
                "arrival": 1675790400000
              }
            }
          },
          "upc": "0001234567890",
          "sku": "RW-001",
          "taxable": true,
          "variantId": "variant1"
        }
      ]
    }
  4. Based on the response shape, complete the selection mapping. Since the response is a single object, you can directly map its fields to Product type fields without $. See the mapping overview to learn more about how to use the mapping language.

    GraphQL
    products.graphql
    type Query {
      products: [Product]
        @connect(# -- snip -- #)
    
      product(id: ID!): Product
        @connect(
          source: "ecomm"
          http: { GET: "/products/{$args.id}" }
          selection: """
          id
          name
          description
          """
        )
    }
  5. Save your products.graphql.

That's it—you've created a connector from scratch!

Run a request

Run the following query in your Sandbox to get a single product by id:

GraphQL
Sandbox request
query Product {
  product(id: "1") {
    id
    name
    description
  }
}

You should see results for just a single product with id 1. You can update the id to view results for other products. The next connector showcases how you can orchestrate multiple REST API calls with just one GraphQL request.

Orchestrate calls with connectors

The ecommerce demo API has a /products/:id/reviews endpoint to retrieve a particular product's reviews. Check out /products/1/reviews for an example.

/products/1/reviews
JSON
{
  "reviews": [
    {
      "id": 1,
      "rating": 5,
      "comment": "These wheels are perfect for my lunar exploration project!",
      "author": "John D",
      "createdAt": 1675200000000
    },
    {
      "id": 2,
      "rating": 4,
      "comment": "Solid build quality, just wish they were a little lighter.",
      "author": "Emma H",
      "createdAt": 1675200000000
    },
    /// ...
  ],
  "summary": {
    "total": 10,
    "averageRating": 4
  }
}

This endpoint returns a list of reviews under the reviews key, each with its own ID, numerical rating, comment, author, and createdAt timestamp. To display a product detail page with reviews, clients normally have to first query the /products/:id endpoint for product details and then the /products/:id/reviews for the product's reviews.

With connectors, the client can make one request, asking for all the information at once. Behind the scenes, the graph is orchestrating the calls and combining the information into one client-friendly response. To define what the client response should look like, you need to update the GraphQL schema:

  1. Add a new Review type underneath the Product type in your products.graphql schema.

    GraphQL
    products.graphql
    extend schema
    # -- snip -- #
    
    type Product {
      id: ID!
      name: String
      description: String
    }
    
    type Review {
      id: ID!
      rating: Float
      comment: String
    }
  2. Since one product has many reviews, you define that relationship by adding a reviews field to the Product type. It returns a list of Review objects.

    GraphQL
    products.graphql
    extend schema
    # -- snip -- #
    
    type Product {
      id: ID!
      name: String
      description: String
      reviews: [Review]
    }
    
    type Review {
      id: ID!
      rating: Float!
      comment: String
    }
  3. To retrieve a product's reviews, add a connector to the reviews field in the Product type.

    GraphQL
    products.graphql
    extend schema
    # -- snip -- #
    
    type Product {
      id: ID!
      name: String
      description: String
      reviews: [Review]
        @connect(
          source: "ecomm"
          http: { GET: "/products/{$this.id}/reviews" }
          selection: """
          $.reviews {
            id
            rating
            comment
          }
          """
        )
    }

    What's $this? Similar to the $args variable you used to access arguments for the /products/{$args.id} path when creating a connector for Query.product, you can use $this to access an object's fields when creating a connector for a field on a root type like Product.

That's it! Save your updated schema to see orchestration in action.

Run a request

Run a new query in your Sandbox to get a product's details and reviews:

GraphQL
Sandbox request
query ProductWithRating {
  product(id: "1") {
    id
    name
    description
    reviews {
      id
      rating
      comment
    }
  }
}

You should see results for a single product, including its reviews. The Sandbox offers additional views in the right panel to help you understand what's happening behind the scenes.

Inspect a query plan

Click Response and select Query Plan to inspect a visual representation of the steps taken to resolve a request.

Viewing a query plan in Apollo Sandbox

A query plan is created by the query planner, the part of the GraphOS Router that efficiently breaks down a GraphQL request into multiple, coordinated calls to various endpoints and services. Despite its name, the query planner handles all types of GraphQL operations—not just queries.

Because the request requires two calls to two separate endpoints, the query planner automatically sequences API calls (the two Fetch nodes) and then combines the results (the Flatten node).

The next connector builds on the orchestration example by combining data from two endpoints about the same object type.

Enrich entities with connectors

If you compare the product data returned by /products and /products/1, you'll notice that /products/:id returns many more fields than /products.

Show me the data
JSON
/products/
 {
      "id": 1,
      "name": "Lunar Rover Wheels",
      "createdAt": 1636742972000,
      "updatedAt": 1636742972000,
      "description": "Designed for traversing the rugged terrain of the moon, these wheels provide unrivaled traction and durability. Made from a lightweight composite, they ensure your rover is agile in challenging conditions.",
      "slug": "lunar-rover-wheels",
      "tags": [
        {
          "tagId": "space",
          "name": "Space"
        },
        {
          "tagId": "engineering",
          "name": "Engineering"
        },
        {
          "tagId": "rover",
          "name": "Rover"
        }
      ],
      "category": "Engineering Components",
      "availability": "AVAILABLE"
    },
JSON
/products/1
{
  "id": 1,
  "name": "Lunar Rover Wheels",
  "createdAt": 1675200000000,
  "updatedAt": 1675200000000,
  "description": "Innovatively designed wheels for lunar rovers, built to endure harsh moon terrain and provide optimal agility. Each wheel is constructed using advanced materials to withstand temperature fluctuations and dust.",
  "slug": "lunar-rover-wheels",
  "tags": [
    {
      "tagId": "1",
      "name": "Instruments"
    },
    {
      "tagId": "2",
      "name": "Space"
    }
  ],
  "category": "Engineering Components",
  "availability": "AVAILABLE",
  "variants": [
    {
      "variantId": "variant1",
      "name": "Standard Wheel",
      "price": {
        "original": 4999,
        "discounts": [],
        "final": 4999
      },
      "specifications": {
        "Material": {
          "value": "Titanium alloy"
        },
        "Diameter": {
          "value": "50 cm"
        }
      },
      "inventory": {
        "quantity": 100,
        "sellUnavailable": false
      },
      "shipping": {
        "ship1": {
          "weight": 5,
          "method": "GROUND",
          "estimate": {
            "price": 499,
            "arrival": 1675804800000
          }
        },
        "ship2": {
          "weight": 5,
          "method": "AIR",
          "estimate": {
            "price": 999,
            "arrival": 1675790400000
          }
        }
      },
      "upc": "0001234567890",
      "sku": "RW-001",
      "taxable": true
    }
  ]
}

This is a common pattern in REST APIs. Product listing pages let users browse multiple products at once, while product detail pages provide more comprehensive information about individual products.

Suppose your team wants to redesign the listing page to show swatches for different product variants, but that information is only available from the product detail endpoint. You can use connectors to still make one request for the product listing page without having to change the underlying APIs. Specifically connectors can coordinate the following tasks:

  • Calling the product listing endpoint (/products) to retrieve all products

  • Calling the product detail endpoints (/products/:id) to retrieve variant information for all products

  • Combining the data together into one response to the client

GraphQL federation uses entity types to represent a single object type that combines information from multiple data sources.

Working with entities

An entity is any object with data fields that can be fetched with one or more unique key fields, much like objects or rows in a database. To define an entity type in a GraphQL schema, you use the @key directive, followed by the unique identifier field(s). An entity type must also specify how to retrieve the data for its fields. You can use connectors to accomplish this.

  1. Update your schema so that the Product type uses the @key directive. The product's id field works well as its unique identifier.

    GraphQL
    products.graphql
    extend schema
    # -- snip -- #
    
    type Product @key(fields: "id") {
      id: ID!
      # -- all the other fields -- #
    }
  2. To the same Product type, add the variants field that's only available from the product detail endpoint. Then add the corresponding Variant type.

    GraphQL
    products.graphql
    extend schema
    # -- snip -- #
    
    type Product @key(fields: "id") {
      id: ID!
      # -- all the other fields -- #
      variants: [Variant]
    }
    
    type Variant {
      id: ID!
      name: String
    }
  3. Add the variants field to your selection mapping for the Query.product connector.

    GraphQL
    products.graphql
    type Query {
      products: [Product]
        @connect(# -- snip -- #)
    
      product(id: ID!): Product
      @connect(
        source: "ecomm"
        http: { GET: "/products/{$args.id}" }
        selection: """
        id
        name
        description
        variants {
          id: variantId
          name
        }
        """
      )
    }
    note
    Since /products/:id returns variant IDs nested as variant.variantId, we need to rename the field to match the GraphQL schema.
  4. Finally, add entity:true to the Query.product connector.

    GraphQL
    products.graphql
    type Query {
      products: [Product]
        @connect(# -- snip -- #)
    
      product(id: ID!): Product
      @connect(
        source: "ecomm"
        http: { GET: "/products/{$args.id}" }
        selection: """
        id
        name
        description
        variants {
          id: variantId
          name
        }
        """
        entity: true
      )
    }

The entity:true part indicates that the connector can use the unique key (the id from the /products endpoint) to merge the correct information together. All together, these additions provide the necessary instructions for how to retrieve any product fields you want, whether you're writing a query for one product or all products.

Run a request

Run a new query in your Sandbox to get information for all products, including each product's variants:

GraphQL
Sandbox request
query ProductsWithVariants {
  products {
    id
    name
    description
    variants {
      id
      name
    }
  }
}

In the right panel, you can see all product fields returned, including the variants information, into a single response.

Schema Visualization in GraphOS Studio

By checking out the Query Plan, you can see how connectors are fetching data from two different endpoints and merging their data intelligently into the response.

Schema Visualization in GraphOS Studio

Connectors summary

Before moving on to some additional capabilities of Rover and GraphOS, let's summarize the connectors you've developed:

  1. The existing example connector fetches fields from the high-level /products endpoint.

  2. The argument connector fetches fields from the /products/:id endpoint.

  3. The first orchestration connector adds review data to products from the /products/:id/reviews endpoint.

  4. The entities connector combines data from /products and /products/:id endpoints for a more complete representation of the Product entity type.

Any combination of data from all these endpoints is available to all your clients with a single API request to the GraphQL API you've been developing locally. Next, you'll learn how to publish your updated schema to the graph registry in GraphOS to share your graph with your team.

Step 3. Publish your graph

Throughout this guide, you've updated products.graphql to expand your graph's capabilities. To share your updates with the rest of your GraphOS organization and to get your updated schema into GraphOS's schema management pipeline, you need to publish it to GraphOS.

You publish schemas using the rover subgraph publish command:

  1. Copy the following multi-line command. Replace <APOLLO_KEY> <GRAPH_REF> with key the graph ref from the multi-line rover dev command you ran at the beginning of the guide.

    terminal
    APOLLO_KEY=<APOLLO_KEY> \
    rover subgraph publish <GRAPH_REF> \
    --schema ./products.graphql \
    --name products \
    --routing-url http://ignored
  2. Paste and run your updated multi-line command. Once the command completes, you'll see the following in your terminal:

    terminal
    A new subgraph called 'products' was created in <GRAPH_REF>
    The supergraph schema for <GRAPH_REF> was updated,
    composed from the updated 'products' subgraph
    Monitor your schema delivery progression on studio: <STUDIO_URL>

Nice! If you go to the <STUDIO_URL> provided, you land on your graphs Launches page in GraphOS Studio. With this updated schema published, you can further explore your graph in Studio and share it with your team by inviting them to your organization.

For example, from the Schema tab, your team can view a Visualization of the types and relationships in your graph.

Schema Visualization in GraphOS Studio

Once you start collecting metrics, you can use the visualization tool to create heatmaps to identify fields that are most frequently used, or have the highest latency or errors. You can also analyze detailed information about your operations usage and performance from the Insights tab:

Operation usage metrics and insights in GraphOS Studio

Next steps

Depending on your goals, you have several options for learning more about Apollo Connectors and GraphOS' capabilities.

Connectors resources

odyssey tutorial
If you learn best with videos and exercises, this interactive course teaches you how to bring an existing REST API into a GraphQL API using Apollo Connectors. It covers the same essentials as this guide, but goes into more detail and has additional sections such as how to use connectors to make POST requests.

GraphOS resources

To learn more about Studio features, including schema checks, linting, and schema proposals, check out the GraphOS platform documentation. If you'd like a visual tour of some of these features, check out the video below.

Watch the video tour
Feedback

Forums