Getting Started with Apollo Federation and Gateway
Tags: javascript, graphql, apollo
Published: 2020-04-09 03:23:53

Last year, Apollo released an open-source tool called [Apollo Federation](https://www.apollographql.com/docs/apollo-server/federation/introduction/) to help simplify the process of composing multiple GraphQL APIs into a single gateway API. 

Having used schema stitching to join GraphQL APIs together in the past, the declarative, no-fuss approach offered by Apollo Federation was a breath of fresh air. In fact, at the time this library was released, I had recently started [writing a book about GraphQL](https://8bit.press/book/advanced-graphql) and promptly rewrote the first part to use Apollo Federation instead.

Having spent the last 10 months exploring this library, I thought I would write a series of blog posts here to share some tips about what I've learned along the way.

In the first post, I'll provide a brief overview of how to set up two "federated schemas" in separate services using Apollo Federation and then combine them into a single GraphQL API using Apollo Gateway. I'll also share my preferred approach for setting up npm scripts to start and reload the gateway API and the two implementing services.

**TL;DR** You can find the [the complete code here.](https://github.com/mandiwise/basic-apollo-federation-demo)

Our first step will be to create a project directory:

```sh
mkdir basic-apollo-federation-demo && cd basic-apollo-federation-demo
```

Then we'll run `npm init` in the new directory (the `--yes` flag creates the `package.json` file without asking any questions):

```sh
npm init --yes
```

Next, we'll install all of the packages we need:

```sh
npm i apollo-server@2.14.2 @apollo/federation@0.13.2 @apollo/gateway@0.13.2 \
esm@3.2.25 graphql@15.0.0 nodemon@2.0.2 concurrently@5.1.0 wait-on@4.0.1
```

Here's an explanation of what the above packages will be used for:

- `apollo-server`: We'll need an instance of `ApolloServer` for the gateway API and each of the services we create.
- `@apollo/federation`: This package will allow us to make our services' schemas composable.
- `@apollo/gateway`: This package will distribute incoming GraphQL API requests to underlying services.
- `graphql`: Apollo requires this library as a peer dependency.
- `esm`: This package is a "babel-less, bundle-less ECMAScript module loader" that will allow us to use `import` and `export` in Node.js without any hassle.
- `nodemon`: Nodemon will automatically reload our application when files change in the project directory.
- `concurrently`: We can run multiple commands at the same time using this package. It also has support for shortened commands with wildcards.
- `wait-on`: It would be a good idea to wait and make sure that the implementing services' ports are available before starting the gateway API, so we'll use this package for that.

Next, we'll need to create some directories and files to organize our project. To set the scene (no pun intended 🙃), we're going to create a mini IMDB-like API that provides data about films, actors, and directors. The beauty of Apollo Federation is that it allows you to split an API based on _separation of concerns_ rather than being limited to breaking up a GraphQL API by types.

In practice, that means we can define a type in one service's schema and access it or even extend it with additional fields in another. This feature makes it much easier to split an API logically by product area.

We'll manage access to our data via separate "films" and "people" services. Each service will have a federated schema, and we will merge those two schemas into the gateway-level API so that clients can query data from a single API without any direct concern for the two underlying services.

Let's add a directory for each of the services now:

```sh
mkdir films people
```
We'll also add `index.js` files to contain the code for the two services and the gateway:

```sh
touch index.js films/index.js people/index.js
```

Lastly, we'll need some mock data to query via the API. Add a `data.js` file too:

```sh
touch data.js
```

And add this code to it:

```js
export const people = [
  { id: "1", name: "Steven Spielberg" },
  { id: "2", name: "Richard Dreyfuss" },
  { id: "3", name: "Harrison Ford" },
];

export const films = [
  {
    id: "1",
    title: "Jaws",
    actors: ["2"],
    director: "1",
  },
  {
    id: "2",
    title: "Close Encounters of the Third Kind",
    actors: ["2"],
    director: "1",
  },
  {
    id: "3",
    title: "Raiders of the Lost Ark",
    actors: ["3"],
    director: "1",
  },
];
```

We'll start by setting up the people service. Add the following code to `people/index.js`:

```sh
import { ApolloServer, gql } from "apollo-server";
import { buildFederatedSchema } from "@apollo/federation";

import { people } from "../data.js";

const port = 4001;

const typeDefs = gql`
  type Person @key(fields: "id") {
    id: ID!
    name: String
  }

  extend type Query {
    person(id: ID!): Person
    people: [Person]
  }
`;
```

Above, we have a basic set of type definitions to describe a `Person` in our API. A `Person` can be either an actor or a director, but we'll leave it up to the films service to make that distinction later. You'll see a `@key` directive has been added to the `Person` type definition—this special directive makes `Person` an _entity_ and it's the way we tell Apollo that this type can be referenced and extended by other services (as long as the other services can identify a person by the value represented by their `id` field).

There are two other things to note in this file. The first is that we import `buildFederatedSchema` from `@apollo/federation` so we can later make our schema federation-ready. The second is that we use the `extend` keyword in front of `type Query` because the `Query` and `Mutation` types originate at the gateway level so the Apollo documentation says that all implementing services should extend these types with any additional operations.

Next, we'll add some resolvers for the types in `people/index.js`:

```js
// ...

const resolvers = {
  Person: {
    __resolveReference(object) {
      return people.find((person) => person.id === object.id);
    }
  },
  Query: {
    person(_, { id }) {
      return people.find((person) => person.id === id);
    },
    people() {
      return people;
    }
  }
};
```

The resolvers for `Query` are what we would expect to see, but we encounter something interesting under `Person` with `__referenceResolver`. This _reference resolver_ is how we explain to the gateway to fetch a person entity by its `@key` field (which is the `id`) when referenced by other services.

Lastly, we'll kick off a new `ApolloServer` for this service at the bottom of `people/index.js`, using the return value of `buildFederatedSchema` for the `schema` option in the server, rather than passing in the `typeDefs` and `resolvers` explicitly:

```js
// ...

const server = new ApolloServer({
  schema: buildFederatedSchema([{ typeDefs, resolvers }]),
});

server.listen({ port }).then(({ url }) => {
  console.log(`People service ready at ${url}`);
});
```

This is all the code we need for our people service. Before we turn our attention to the films service, we'll set up the gateway API in `index.js`:

```js
import { ApolloGateway } from "@apollo/gateway";
import { ApolloServer } from "apollo-server";

const port = 4000;

const gateway = new ApolloGateway({
  serviceList: [
    { name: "people", url: "http://localhost:4001" }
  ]
});

const server = new ApolloServer({
  gateway,
  subscriptions: false
});

server.listen({ port }).then(({ url }) => {
  console.log(`Server ready at ${url}`);
});
```

At the gateway level we once again instantiate an `ApolloServer`, but this time we have also imported and instantiated an `ApolloGateway` and passed that into the `ApolloServer` instead of a schema. The `ApolloGateway` constructor is passed a `serviceList` array of objects where each object describes one of the federated schemas we want to compose in the gateway. Lastly, we set `subscriptions` to `false` in this `ApolloServer` because Apollo Gateway does not support subscriptions at this time.

With our current code in place, we can start up our GraphQL API for the first time. To do that, we'll need to update `scripts` in `package.json` by creating three new scripts. We create a `dev:people` script to start up the people service with `nodemon` and a `dev:gateway` script that waits for the people service's port to be available, then starts the gateway API up using `nodemon` too. Finally, we create a `dev` script that uses `concurrently` to start up all `dev:-` scripts using a wildcard:

```json
{
  ...
  "scripts": {
    "dev": "concurrently -k npm:dev:*",
    "dev:people": "nodemon -r esm ./people/index.js",
    "dev:gateway": "wait-on tcp:4001 && nodemon -r esm ./index.js"
  },
  ...
}
```

Note that we use the `-r` (or `--require`) flag for both the people service and gateway API processes to preload the `esm` module when running Node.js (as is required by the esm package).

Try running `npm run dev` now to make sure that the GraphQL API is available at http://localhost:4000/graphql. You will be able to open GraphQL Playground at this URL in your browser now too.

Next, we'll build out the films service. The schema for the films service will be more involved than the people service because, in addition to adding a `Film` type, it will both reference and extend the previously created `Person` type. First, we'll set up the imports and type definitions in `films/index.js`:

```js
import { ApolloServer, gql } from "apollo-server";
import { buildFederatedSchema } from "@apollo/federation";

import { films } from "../data.js";

const port = 4002;

const typeDefs = gql`
  type Film {
    id: ID!
    title: String
    actors: [Person]
    director: Person
  }

  extend type Person @key(fields: "id") {
    id: ID! @external
    appearedIn: [Film]
    directed: [Film]
  }

  extend type Query {
    film(id: ID!): Film
    films: [Film]
  }
`;
```

To use the `Person` type in the film service we have to define it again, but this time we put the `extend` keyword in front of it. We also have to include its key field of `id`, but use this time add the `@external` directive to indicate that it was defined in another service. After that, we add two new fields to the `Person` type so we can list what films the person appeared in or directed.

In the `Film` type, we can also use the `Person` object to list people who acted in or directed the film, but this time in relation to that film. By both referencing and extending the `Person` type in the films service, the relationships between people and films can be traversed in both directions in the data graph even though their corresponding types are defined in different services.

Next, we'll need to write resolvers for all of the new types and extra fields added by the films service. Add the following code to `films/index.js`:

```js
// ...

const resolvers = {
  Film: {
    actors(film) {
      return film.actors.map((actor) => ({ __typename: "Person", id: actor }));
    },
    director(film) {
      return { __typename: "Person", id: film.director };
    }
  },
  Person: {
    appearedIn(person) {
      return films.filter((film) =>
        film.actors.find((actor) => actor === person.id)
      );
    },
    directed(person) {
      return films.filter((film) => film.director === person.id);
    }
  },
  Query: {
    film(_, { id }) {
      return films.find((film) => film.id === id);
    },
    films() {
      return films;
    }
  }
};
```

When resolving the `actors` and `directors` fields on `Film` the only information the film service has about those people is their unique ID, but that's OK! To resolve these fields with Apollo Federation, we only need to return an object (or list of objects) containing the `__typename` and the key field/value to identify that object when the request is forwarded to the people service.

Additionally, even though the `Person` type is initially defined by another service, we need to resolve the new fields that the films service adds here by matching the person's ID to any films where their ID matches the `director` ID or appears in the `actors` array.

The final piece of code to add in `films/index.js` starts the `ApolloServer` for this service, just as we did in the people service:

```js
// ...

const server = new ApolloServer({
  schema: buildFederatedSchema([{ typeDefs, resolvers }]),
});

server.listen({ port }).then(({ url }) => {
  console.log(`Films service ready at ${url}`);
});
```

We'll need to add the films service to the gateway API now in `index.js`:

```js
// ...

const gateway = new ApolloGateway({
  serviceList: [
    { name: "people", url: "http://localhost:4001" },
    { name: "films", url: "http://localhost:4002" } // NEW!
  ]
});

// ...
```

Lastly, we'll add another npm script in `package.json` to start the films service and also require the gateway to wait for the films service's port now:

```json
{
  ...
  "scripts": {
    "dev": "concurrently -k npm:dev:*",
    "dev:people": "nodemon -r esm ./people/index.js",
    "dev:films": "nodemon -r esm ./films/index.js",
    "dev:gateway": "wait-on tcp:4001 tcp:4002 && nodemon -r esm ./index.js"
  },
  ...
}
```

Our GraphQL API is now ready to go—try out some queries in GraphQL Playground to make sure you can query people and films as expected. You can see [the final version of the code here](https://github.com/mandiwise/basic-apollo-federation-demo) as a reference.

I hope this post has given you a glimpse of how approachable Apollo Federation is if you have a bit of prior experience with Apollo Server. Thanks for coding along!