9. Apollo Connectors
15m

Overview

In this section, we'll cover how to add the Orders REST API to our without using a .

Prerequisites

  • Our running in the cloud

Apollo Connectors for REST APIs

Connectors are a new declarative programming model for , allowing you to plug your existing REST services directly into your graph. Once integrated, client developers gain all the benefits of GraphQL, and API owners gain all the benefits of , including incorporation into a for a comprehensive, unified view of your organization's data and services.

For more information, refer to the Apollo Connectors documentation.

Apollo Connectors: KBT Threads and the Orders API

Because our Orders API is a REST-based service, is it a great candidate for Apollo Connectors, but it's always best to check whether an API is a good fit or not.

Using Apollo Connectors with the Orders API

Let's revisit the Orders schema once again. If you need to reset it, you can copy the schema from the code below (comments have been removed for brevity). We have also intentionally left the Authentication and Authorization in place.

orders.graphql
extend schema
@link(url: "https://specs.apollo.dev/federation/v2.8", import:
[
"@key",
"@tag",
"@shareable"
"@authenticated",
"@requiresScopes",
]
)
type Query {
order(id: ID!): Order @authenticated
}
type Order @key(fields: "id") {
id: ID!
buyer: User! @requiresScopes(scopes: [["order:buyer"]])
items: [ProductVariant!]! @requiresScopes(scopes: [["order:items"]])
}
type User @key(fields: "id") {
id: ID!
}
type ProductVariant @key(fields: "id", resolvable: false) {
id: ID!
}

Connectors checklist

Using the Proposals editor to change the Orders schema

The latest release of the Proposals editor composes your in real-time as you make changes to your schema, making it ideal for developing in a safe environment. For this lab, we will use this editor to make changes to the orders.graphql file, which will then be committed to the repository.

Note: if you don't want to use the Proposals editor, you can make changes directly in the Github editor.

1. ✏️ Create a new proposal

Start by creating a new proposal and give it a name, such as "Connect the Orders REST API to the ":

studio.apollographql.com

Creating a new proposal to edit the schema

Then, choose the current that was created for your account. You can optionally enter a description. Finally click on the Create Proposal button.

Once the Proposal editor is open, select the orders on the right-hand side to get started:

studio.apollographql.com

Choosing the orders schema

2. ✏️ Make sure "Compose on Change" is enabled in the editor

This feature allows you to see how your changes will affect the , and avoid any potential issues. To enable this feature, simply check the "Compose on Change" box in the editor toolbar, as shown below:

studio.apollographql.com

Making sure Compose on Change is on

Work checklist

3. ✏️ Federation version and directives for Apollo Connectors

With the editor open, let's start by changing the Federation version - which is currently set to 2.8 - to 2.10. This will allow us to use the latest features of . Update the @link as follows:

-@link(url: "https://specs.apollo.dev/federation/v2.8", import:
+@link(url: "https://specs.apollo.dev/federation/v2.10", import:

Next, in order to be able to make use of the Apollo Connector directives, we need to import them using a new @link . Add the following highlighted code directly below the first @link , as shown below:

orders.graphql
extend schema
@link(
url: "https://specs.apollo.dev/federation/v2.10"
import: ["@key", "@tag", "@shareable", "@authenticated", "@requiresScopes"]
)
@link(
url: "https://specs.apollo.dev/connect/v0.1"
import: ["@connect", "@source"]
)

Heads up! As soon as you add the new @link , the editor will automatically start to compose the , and this will result in a lot of errors! Don't worry though, we will fix them in the next steps.

As you can see from the above, we can now make use of the @connect and @source . These directives are used to define the connection between the schema and the REST API. Check the Apollo Connectors documentation for more information on these .

4. Add the @source directive to the schema

The Apollo Connector directives allow us to define the connection between the schema and the REST API. The @source is used to define the REST API endpoint that the schema will connect to - so let's make use of it.

Add the @source directly below our last @link as shown below:

orders.graphql
# ...
@link(
url: "https://specs.apollo.dev/connect/v0.1"
import: ["@connect", "@source"]
)
@source(
name: "api"
http: { baseURL: "https://rest-api-j3nprurqka-uc.a.run.app/api" }
)
studio.apollographql.com

Making sure Compose on Change is on

If you'd like to check the REST API endpoint, you can use the following URL: https://rest-api-j3nprurqka-uc.a.run.app/api/orders/1. Opening this URL in your browser should return a JSON response with the order details:

{
"id": 1,
"customerId": 10,
"variantIds": ["27", "11", "347"]
}

Now that we have a source API defined and we know the result of invoking our REST API, we are ready to start mapping our schema to the REST API.

5. Add the @connect directive to the schema

We will add the @connect right after the @authenticated in our order(id: ID!) . Note that as soon as you type @conn the editor will suggest to autocomplete it for you - just press Enter to accept the suggestion. You can also press Ctrl + Space to see the available suggestions.

studio.apollographql.com

Editor autocompleting the connect directive

The editor will place the caret in the source: "" . This is where you will define the connection between the schema and the source REST API. The source should match the name in the @source , which is "api" in this case. Go ahead and set the source to "api".

Hint: you can use ➡️ TAB to cycle between the various that need to be filled in the .

Next, we'll define what type of verb to use for our REST endpoint - in this case, it's a GET . Pressing TAB will take us to the next , the relative path.

Let's take a look once again at the test URL: https://rest-api-j3nprurqka-uc.a.run.app/api/orders/1

  • we have set the base URL to https://rest-api-j3nprurqka-uc.a.run.app/api in our @source , therefore
  • the relative path for this is /orders/1

After making these changes, our should look like this:

type Query {
order(id: ID!): Order
@authenticated
@connect(
source: "api"
http: { GET: "/orders/{$args.id}" }
selection: """
"""
)
}

6. Define the selection set

The selection is where we map the fields returned by the REST API to our . From our JSON response above, we know that the API returns id, customerId, and variantIds. In turn, our defines that the Order type has three : id of type String, buyer of type User, and items of type ProductVariant.

Mapping the id is quite straightforward since we have the exact same field. Let's start by adding the id to our selection set. Add the id directly into the selection as shown below:

type Query {
order(id: ID!): Order
@authenticated
@connect(
source: "api"
http: { GET: "/orders/{$args.id}" }
selection: """
id
"""
)
}

Note: immediately after adding the id , the buyer and items will display red squiggly lines beneath them. This occurs because we have not yet defined these fields in the :

studio.apollographql.com

Red squiggly lines for buyer and items fields

What about buyer and items then? In any REST scenario, this would involve making additional requests to the API to retrieve the User and ProductVariant details. However, thanks to Federation and the Apollo , all we need to do is return the IDs of both the User and ProductVariant, and the Apollo will handle the rest. It does that by using the @key on the types to resolve the IDs to the actual objects using the other in a .

From the above, for the Apollo to resolve an using its ID we need to return a value of the form:

{ "id": "the-id-of-the-entity" }

For more information on Apollo Connectors and Federation, check the Reference entities section of the Apollo Connectors documentation.

Let's start by asking the Apollo to resolve the buyer. To do this, we need to add this line in the :

type Query {
order(id: ID!): Order
@authenticated
@connect(
source: "api"
http: { GET: "/orders/{$args.id}" }
selection: """
id
buyer: { id: $.customerId }
"""
)
}

Note: the $ is a special symbol used to hold the value enclosed by the parent {...} selection, or the root value at the top level. See the Variables section in the Apollo Connectors documentation for more details.

The { ... } syntax allows us to create a new object in the . In this case, we are creating a new object with the id set to the customerId returned by the REST API.

Conversely, the items is an array of ProductVariant entities. We can use the same syntax to create an array of objects in the . The variantIds returned by the REST API is an array of String values, so in this case we can use a special syntax that allows to map these values as an array of key-value pairs.

Let's map the items in our order to the JSON payload in the :

type Query {
order(id: ID!): Order
@authenticated
@connect(
source: "api"
http: { GET: "/orders/{$args.id}" }
selection: """
id
buyer: { id: customerId }
items: $.variantIds { id: $ }
"""
)
}

The $.variantIds { ... } syntax allows us to iterate over the variantIds array and return an array of objects with the id set to the value of each element in the array. See the Wrapping fields section for more details on this syntax.

Panic!!😱😱 Composition is still returning an error!

Yes indeed - we are still missing one crucial piece of information! In our schema design, the order(id: ID!) is designed also as an ! Typically with a , you would create a special method to resolve this, in the form of __resolveReference. However, with Apollo Connectors, we can mark our as an by adding the entity: true at the end of our @connect , like so:

type Query {
order(id: ID!): Order
@authenticated
@connect(
source: "api"
http: { GET: "/orders/{$args.id}" }
selection: """
id
buyer: { id: $.customerId }
items: $.variantIds { id: $ }
"""
entity: true
)
}

When entity: true is set on a connector, its provides an for ning. Check the rules for entity: true in the Apollo Connectors documentation.

And that's it! 🎉🎉🎉 Check the composition status in your editor. If there are no errors, we should be ready to go:

studio.apollographql.com

No composition errors in editor

Now copy the contents of the Proposals editor and head to the Github page. Open the orders-schema.graphql file and paste the contents of the Proposals editor into the file. Once you have pasted the contents, commit the changes.

studio.apollographql.com

Github editor with new contents

Once the file has been commmited, the changes will be automatically applied to the . You can check the status of the publish job in the Actions tab of the repository. If the workflow workflow has completed successfully, we can now head over to Apollo Studio to test our new schema.

Check your work: testing the new schema

Let's make a request to retrieve an order's buyer and items.

  1. Create a new tab and copy the below:

    query order($orderId: ID!) {
    order(id: $orderId) {
    id
    buyer {
    email
    firstName
    }
    items {
    price
    size
    colorway
    id
    }
    }
    }
  2. In the Variables panel, copy and paste the following JSON payload:

    { "orderId": "1" }
  3. Add an the Authorization header (if not present already) and set the value to Bearer {{authorizedToken}}.

Bearer {{authorizedToken}}

Send the request - we should see that data has successfully been returned!

studio.apollographql.com

Screenshot of a successful operation

But wait, how do we actually know that the data is coming from the REST API and not the Order subgraph? 🤔

Head over to Subgraphs in Apollo Studio. You should see that the entry for Order is different: this is because we have used Apollo Connectors to connect the Order schema to the REST API directly:

studio.apollographql.com

The new Orders server-free subgraph

Conclusion

🎉 Congratulations on completing this lab! 🎉 By using Apollo Connectors you have now created a server-free implementation of a GraphQL subgraph - still part of a Supergraph, saving resources by avoiding additional deployments and development time.

With Apollo Connectors,

  • you no longer need a specific service and code to integrate REST services into your . This simplifies integrating the many APIs that are nothing more than passthrough services to REST APIs.
  • you can leverage the power of to efficiently orchestrate calls to multiple services and compose the results into a single response.
  • you can define your REST integration in your schema and let the handle the rest.

Workshop conclusion

Congratulations on successfully completing this workshop! 🎉🎉🎉

Throughout this journey, we have now added:

  • ,
  • authentication and authorization,
  • a coprocessor,
  • limits,
  • ,
  • safelisting, and
  • Apollo Connectors!

We did all of this with either schema changes or yaml configuration. With those simple changes, we were able to create a that serves more use cases and does so in a more secure way.

Beyond that, we have also begun the process of detailing how we can manage schema changes for a across multiple stakeholders to ensure our graph remains highly available.

KBT Threads can now confidently deploy their to support their new omni-channel presence!

Previous