April 22, 2020
An under-appreciated aspect of the GraphQL origin story is that Facebook.com is a monolithic application with a monolithic data store. Their GraphQL API is “simply” a layer in that monolith.
Many companies, Square included, use a microservice architecture instead. We have thousands of services (in multiple languages) and hundreds of data stores (of multiple types). We see this distributed, polyglot architecture as an advantage as we grow into a global ecosystem providing a variety of products and experiences, including small business tools, peer-to-peer payments, and banking. Adding a single GraphQL API over all of that data is an overwhelming task.
Or it was—until Apollo announced Federation.
Federation is a declarative model for graph composition of loosely coupled downstream GraphQL services that enables static composition and validation of a unified graph using query plans to resolve downstream operations.
— The Architecture of Federation
The core concept behind Federation is the ability to “compose” multiple GraphQL APIs into a single API automatically without having to write and maintain complex “stitching” code. The query planner provided by the @apollo/gateway library can efficiently fetch and combine data from multiple sources.
As I’ve explored Federation for Square’s GraphQL APIs, I’ve learned to appreciate the power of the query planner. I’ve uncovered some interesting and sometimes non-obvious use cases I’d like to share here. I think there’s a ton of potential to simplify architectures and reduce coupling between services—all while providing a great product-centric API for applications and devices.
I’m planning on a series of articles, each covering a different pattern. Some patterns we plan to put into production, while others are just experiments.
CodeSandbox.io has made it incredibly easy to explore these patterns. If you want to create your own federated graphs to see how the query planner works, you can fork my template.
The reasons I’m personally invested in Apollo Federation are based on a set of assumptions I’ve come across a number of times in my career:
These assumptions may not be true for your company and its engineering culture! As mentioned above, Assumption #1 is not true for Facebook—they have a monolithic architectural.
But if they are true, you may find yourself with an API Gateway service that’s
difficult to maintain and evolve because it has to support the competing interests
of N number of teams and M number of clients.
Apollo Federation and the @apollo/gateway software dramatically reduce the
maintenance burden of running an API Gateway by declaratively and automatically
composing the concerns of multiple teams in to a single, client-facing API.
Instead of requiring that teams contribute to the same gateway codebase, you can
ask them to provide their functionality in an easy-to-reason-about contract and
let Apollo Graph Manager do the hard work of combining them.
And: web and mobile client developers will love you because they get to leverage all the benefits of GraphQL like flexible queries and type safety!
I have another post that breaks down these assumptions and motivations futher with lots of pictures: Apollo Federation Motivation.
Before we dive into the patterns, let’s talk a bit about how the CodeSandbox template works. I’ve created two types of JavaScript modules:
index.js) that creates an Apollo Server with an Apollo
Gateway. You won’t need to change this much.services/service-a.js and services/service-b.js),
each of which provide a GraphQL schema and resolvers. This is where the fun
stuff happens.As it configures the Apollo Server, index.js looks for any modules in the
services/ folder and registers them as “local services” in the Apollo Gateway.
// index.jsconst localServiceList = walk(`${__dirname}/services`, {directories: false,}).map((file) => require(`${__dirname}/services/${file}`))const gateway = new ApolloGateway({debug: true,localServiceList,buildService: (service) => {return new LocalGraphQLDataSource(buildFederatedSchema([service]))},})
I borrowed this structure from Apollo’s own tests. In a real gateway,
you most likely wouldn’t use localServiceList and LocalGraphQLDataSource,
but they work great for exploring federation in a single NodeJS process.
The simplest service module looks like this:
// services/whatever.jsconst { gql } = require('apollo-server')const typeDefs = gql`type Query {greeting: String}`const resolvers = {Query: {greeting() {return 'Hello world!'},},}module.exports = {name: __filename,resolvers,typeDefs,}
You can create as many modules in services/ as you want and the Gatway will
automatically compose them together. If you write services that are not
compatible—for example, two services that both provide the Query.greeting
field—the Gateway will fail to load. Look for an error in the Terminal that
looks like this:
/sandbox/node_modules/@apollo/gateway/dist/index.js:267throw new apollo_graphql_1.GraphQLSchemaValidationError(errors);^GraphQLSchemaValidationError: Field "Query.greeting" can only be defined once.
After you fix the incompatibility, CodeSandbox will restart the server and you can execute operations against your federated API using the GraphQL Playground.
Hopefully this CodeSandbox template provides an easy on-ramp for exploring Apollo Federation. Now let’s jump in to the first pattern in this series, Relationships!
Written by Lenny Burdette in San Francisco. You can follow him on twitter but he doesn't tweet. Opinions written here do not necessarily reflect those of his employers and are subject to change.