mouton0815 / graphql-federation

Experiments with GraphQL federation using Apollo

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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") {
id: ID!
name: String!
city: String
}
type Author @key(fields: "id") {
id: ID!
books: [Book]
}
Author: {
books: (author) => {
return // all books with authorId == author.id
}
}

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") {
id: ID!
title: String!
authorId: ID!
}
type Book @key(fields: "id") {
id: ID!
authorId: ID! @external
author: Author! @requires(fields: "authorId")
}
Book: {
author: (book) => {
return // author with id == book.authorId
}
}

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
npm install
npm run generate
npm start
cd server-books
npm install
npm run generate
npm start
cd router
npm run compose
npm start

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

About

Experiments with GraphQL federation using Apollo


Languages

Language:TypeScript 92.1%Language:CSS 5.3%Language:JavaScript 1.6%Language:HTML 1.0%