GraphQL Federation with Apollo
This is an experimentation and demonstration project for GraphQL federation with Apollo.
The server part consists of two Apollo subgraph servers, one for authors and one for books. Every subgraph server uses its own separated storage (currently in-memory only). The relationships between authors and books are expressed in the local GraphQL schemas of both servers.
The GraphQL schemas exposed by both subgraph servers are composed to a federated "supergraph" schema by Apollo's rover command line tool. Based on the generated supergraph schema, Apollo's router forwards GraphQL requests to the responsible server(s) and combines the results.
A React client connects to the router and provides a simplistic UI for the management of authors and their books. The React UI is delivered by the serve utility.
Federation Details
Authors and books are managed by different servers, but there are relationships between them:
Every book has an author, modeled as authorId
in the "books" database.
Vice versa, every author has zero or more books.
The latter relationship is not modeled explicitly in the "authors" datbase.
Instead, given the id
of an author, the "books" database finds all relevant books.
The selection of all books of an author is straightforward:
The Author
entity is split between the "authors"
and the "books" schemas. The "authors" schema contains all "core" fields of an author such as id, name, and city.
The "books" GraphQL schema includes the books
field of the Author
entity.
Consequently, the corresponding Author.books
resolver is part of the "books" server:
Author in the "authors" schema | Author part in the "books" schema | Author part of the "books" resolver |
---|---|---|
type Author @key(fields: "id") { |
type Author @key(fields: "id") { |
Author: { |
The selection of the author of a book is similar, but requires some more preparations.
Like Author
, the Book
entity is also split between the "books" and "authors" schemas,
where the "books" schema contains all core fields.
The "authors" schema provides the author
part of the book. To obtain the author, the "authors" resolver
needs the authorId
of the book. To instruct Apollo Federation to include the authorId
into the object
passed to the resolver, it must be marked as @external
:
Book in the "books" schema | Book part in the "authors" schema | Book part of the "authors" resolver |
---|---|---|
type Book @key(fields: "id") { |
type Book @key(fields: "id") { |
Book: { |
Those cross-schema and cross-resolver dependencies couple the subgraph servers and require arrangements between the involved teams.
Preconditions
To run the services of this project, you need
Please make sure that rover
and router
are in your path, so that they are found by the server scripts.
Server Setup
It is best to run the three servers in different shells.
The commands in the router folder must be started last,
because rover
needs the two subgraph servers to compose the supergraph schema.
Shell 1 | Shell 2 | Shell 3 |
---|---|---|
cd server-authors |
cd server-books |
cd router |
The router listens at http://localhost:4000. It runs in dev mode and exposes the Apollo Studio Explorer, which allows to play with GraphQL queries and mutations. An example query for testing is
query Books {
books {
id
title
year
author {
name
}
}
}
Client Setup
From the project root do
cd client
npm install
npm run build
npm start
The frontend is reachable at http://localhost:3000