/

Apollo Federation overview

Implement a single data graph across multiple services


To get the most out of GraphQL, your organization should expose a single data graph that provides a unified interface for querying all of your backing data sources. This allows clients to fetch data from any number of sources simultaneously, without needing to know which data comes from which source.

As your data graph grows, however, it can become inefficient or even difficult to represent the graph with a single, monolithic GraphQL server. To remedy this, you can divide your data graph's implementation across distinct, composable services with Apollo Federation.

Unlike other distributed GraphQL architectures (such as schema stitching), Apollo Federation uses a declarative programming model that makes it easy for each implementing service to implement only the part of your data graph that it should be responsible for. This way, your organization can represent an enterprise-scale data graph as a collection of separately maintained GraphQL services.

Incremental adoption

Like the rest of the Apollo platform, Apollo Federation can (and should) be adopted incrementally:

In both of these cases, all of your clients will continue to work throughout your incremental migration. In fact, clients have no way to distinguish between different data graph implementations.

Apollo Server implementation

Apollo Server supports Apollo Federation via two open-source extensions: @apollo/federation and @apollo/gateway.

  • @apollo/federation provides primitives that your implementing services use to make their individual GraphQL schemas composable.
  • @apollo/gateway enables you to set up an instance of Apollo Server as a gateway that distributes incoming GraphQL operations across one or more implementing services.

Federated schema example

Let's look at an example. Below, we define the schema for a basic e-commerce application as three federated schemas, each of which can be implemented as a standalone GraphQL service:

accounts
extend type Query {
  me: User
}

type User @key(fields: "id") {
  id: ID!
  username: String!
}
products
extend type Query {
  topProducts(first: Int = 5): [Product]
}

type Product @key(fields: "upc") {
  upc: String!
  name: String!
  price: Int
}
reviews
type Review {
  body: String
  author: User @provides(fields: "username")
  product: Product
}

extend type User @key(fields: "id") {
  id: ID! @external
  reviews: [Review]
}

extend type Product @key(fields: "upc") {
  upc: String! @external
  reviews: [Review]
}

These schemas illustrate several important conventions of Apollo Federation:

  • An implementing service can reference a type that's defined by another implementing service. For example, the Review type includes a product field of type Product, even though the Product type is defined in a different service.

  • An implementing service can also extend a type that's defined by another implementing service. For example, the reviews service extends the User type by adding a reviews field to it.

  • An implementing service must add the @key directive to a type's definition in order for other services to be able to reference or extend that type. This directive tells other services which fields to use to uniquely identify a particular instance of the type.

Gateway example

The gateway for our federated data graph fetches the schema from each implementing service and composes those schemas into a single graph:

const gateway = new ApolloGateway({
  serviceList: [
    { name: 'accounts', url: 'http://localhost:4001' },
    { name: 'products', url: 'http://localhost:4002' },
    { name: 'reviews', url: 'http://localhost:4003' }
  ]
});

const server = new ApolloServer({ gateway });
server.listen();

That’s it! With Apollo Federation, schemas and resolvers live in your implementing services. The gateway serves only to plan and execute GraphQL operations across those implementing services.

Query examples

Now we can execute GraphQL operations against our composed schema just as if it were implemented as a monolithic service:

# a query that the gateway can resolve by calling only the products service
query {
  topProducts {
    upc
    name
  }
}
# a query that the gateway must call all three services to fully resolve
query {
  me {
    username
    reviews {
      body
      product {
        name
        upc
      }
    }
  }
}

Next, keep reading to learn more about how composition works and how to build a composed schema. You can also clone a demo schema or check out the CodeSandbox demo in your browser.

Edit on GitHub