Federation quickstart
Part 1 - Local schema composition
In addition to completing this tutorial, you can clone our example gateway on GitHub. The GitHub example is not identical to the gateway you create in this tutorial, but it demonstrates many of the same concepts.
Hello! This tutorial gets you up and running with Apollo Federation. It currently requires Node.js version 12 or 14.
Federation concepts
In a federated architecture, multiple GraphQL APIs are composed into a single federated data graph. The individual APIs are called subgraphs, and they're composed into a supergraph:
Usually, each subgraph corresponds to a different service in your backend. The supergraph is then represented by a gateway, which routes each incoming query to the appropriate combination of subgraphs and returns the combined result.
The supergraph's schema is the combination of each subgraph's schema, plus some special federation-specific directives. Each subgraph's schema can even reference and extend types that originate in a different subgraph. (Learn more)
This architecture enables clients to query data from multiple services simultaneously, just by querying the gateway.
Now that we have a high-level understanding of federation concepts, let's jump in.
1. Install the Rover CLI
Rover is Apollo's CLI for managing data graphs, including federated ones.
Install it like so:
npm install -g @apollo/rover
After installing, run rover
in your terminal with no arguments to confirm that it installed successfully. We'll use various Rover commands in later steps.
2. Create a gateway
You can also clone our example gateway on GitHub. The GitHub example is not identical to the gateway you create in this tutorial, but it demonstrates the same concepts.
As mentioned above, your federated supergraph is represented by a gateway that routes queries to various subgraphs. For this tutorial, we'll use some Apollo-hosted example services as our subgraphs, and we'll set up our own gateway in front of them.
With the help of the @apollo/gateway
library, Apollo Server can act as our federated gateway.
On your development machine, first create a new project directory for your Node.js gateway. Then inside that directory, run the following to create a package.json
file:
npm init
Next, install the following required libraries:
npm install apollo-server @apollo/gateway graphql
Finally, create a file named index.js
and paste the following into it as a minimal (not-yet-functional) gateway implementation:
const { ApolloServer } = require('apollo-server');
const { ApolloGateway } = require('@apollo/gateway');
const supergraphSchema = ''; // TODO!
const gateway = new ApolloGateway({
supergraphSdl: supergraphSchema});
const server = new ApolloServer({
gateway,
// Subscriptions are not currently supported in Apollo Federation
subscriptions: false
});
server.listen().then(({ url }) => {
console.log(`🚀 Gateway ready at ${url}`);
}).catch(err => {console.error(err)});
This code demonstrates the basic flow for creating a gateway:
We initialize an
ApolloGateway
instance and pass it the complete composed schema for our supergraph.- Note the
TODO
. We'll obtain the composed schema in the next section.
- Note the
We initialize an
ApolloServer
instance and pass it our gateway via thegateway
option.- We also disable
subscriptions
because they aren't yet supported in Apollo Federation.
- We also disable
We call
listen
on our server instance to begin listening for incoming requests.
If we run this code as-is with node index.js
, we get an error:
This data graph is missing a valid configuration. Syntax Error: Unexpected <EOF>.
That's because our supergraphSchema
is currently empty! Next, we'll compose that schema.
3. Compose the supergraph schema
As a best practice, your gateway should not be responsible for composing its own supergraph schema. Instead, a separate process should compose the schema and provide it to the gateway. This helps improve reliability and reduce downtime when you make changes to a subgraph.
There are multiple ways to compose a supergraph schema from our subgraph schemas:
- On our local machine using the Rover CLI (we'll start with this)
- Via managed federation in Apollo Studio (we'll switch to this in Part 2)
Using managed federation is strongly recommended for production environments. We'll start with local composition just to get up and running.
Obtaining subgraph details
To compose our supergraph schema, we need the following information about each of our subgraphs:
- The subgraph's schema
- The URL of the subgraph's GraphQL endpoint (which must be accessible by the gateway)
We'll start with just two subgraphs. Because we're using Apollo-hosted example services for our subgraphs, we know their endpoint URLs. We don't have their schemas, however.
Fetching subgraph schemas
We can fetch a running subgraph's schema by executing an enhanced introspection query on it. The Rover CLI provides a command to do exactly that.
From your project directory, run the following command in your terminal:
rover subgraph introspect https://rover.apollo.dev/quickstart/products/graphql
Rover introspects Apollo's example products
service and outputs the following schema:
Now run that same command, but this time append > products.graphql
:
rover subgraph introspect https://rover.apollo.dev/quickstart/products/graphql > products.graphql
This writes the subgraph's schema to a file instead of printing it to your terminal.
Next, do the same thing for the second subgraph (reviews
), this time writing it to reviews.graphql
:
rover subgraph introspect https://rover.apollo.dev/quickstart/reviews/graphql > reviews.graphql
Great! We now have the schemas for our two subgraphs.
Providing subgraph details
After we obtain our subgraph schemas and URLs, we need to provide them to the Rover CLI. To do that, we add them to a YAML file in our project directory.
Create a file called supergraph-config.yaml
and paste the following into it:
subgraphs:
products:
routing_url: https://rover.apollo.dev/quickstart/products/graphql
schema:
file: ./products.graphql
reviews:
routing_url: https://rover.apollo.dev/quickstart/reviews/graphql
schema:
file: ./reviews.graphql
Performing composition
Now that we have all of the information we need, we can compose our supergraph schema. To do that, we'll use another Rover command: supergraph compose
.
From your project directory, run the following command in your terminal:
rover supergraph compose --config ./supergraph-config.yaml
This time, Rover outputs the following schema:
As you can see, this composed schema includes all of the types and fields from our subgraph schemas, along with many additional directives that the gateway uses to support our federated architecture.
Similar to what we did with our subgraph schemas, now append > supergraph.graphql
to the above command to write the composed schema to a file:
rover supergraph compose --config ./supergraph-config.yaml > supergraph.graphql
4. Start the gateway
We can now edit our index.js
file to pull in our composed schema. Replace the file's contents with the following:
const { ApolloServer } = require('apollo-server');
const { ApolloGateway } = require('@apollo/gateway');
const { readFileSync } = require('fs');
const supergraphSchema = readFileSync('./supergraph.graphql').toString();
const gateway = new ApolloGateway({
supergraphSdl: supergraphSchema
});
const server = new ApolloServer({
gateway,
// Subscriptions are not currently supported in Apollo Federation
subscriptions: false
});
server.listen().then(({ url }) => {
console.log(`🚀 Gateway ready at ${url}`);
}).catch(err => {console.error(err)});
Now with our supergraphSchema
properly populated, let's start up the gateway again with node index.js
. This time, there's no error!
We can quickly open our browser to localhost:4000
to view our composed schema in GraphQL Playground:
While we're here, you can even execute some test queries against the supergraph.
Nice job! Our supergraph gateway is running locally and communicating with our Apollo-hosted subgraphs.
Next, we'll move our supergraph composition into Apollo Studio so our gateway can pull schema changes dynamically during runtime. On to part 2!