Apollo Federation in a GraphQL Kotlin Server
Generate GraphQL Schemas Implementing Apollo Federation Specification Directly from Your Code
In many cases, using a single GraphQL API that exposes a unified view of all available data provides tremendous value to API clients. However, as the underlying graph scales, managing a single monolithic GraphQL server might become hard to manage and lead to unnecessary bottlenecks. Migrating towards a federated model with an API gateway and a number of smaller GraphQL services behind it alleviates some of those problems and allows teams to easily scale their graphs.
Apollo Federation
Apollo Federation is an architecture for composing multiple GraphQL services into a single graph. Federated schemas rely on custom directives to instrument the behavior of the underlying graph and convey the relationships between schema types. Each individual GraphQL server generates a valid GraphQL schema and can be run independently. This is in contrast with the traditional schema stitching approach where relationships between individual services, i.e. linking configuration, are configured at the GraphQL Gateway level.
Generating Federated Schemas
The graphql-kotlin-federation library (part of graphql-kotlin) extends the functionality of graphql-kotlin-schema-generator and allows you to easily generate federated GraphQL schemas directly from code. A federated schema is generated by calling the toFederatedSchema function that accepts federated configuration as well as a list of regular queries, mutations and subscriptions exposed by the schema. See my previous post about Creating GraphQL schemas in Kotlin for more details on how to generate base schemas.
All federated directives are available as annotations that decorate your classes, properties and functions. Since federated types might not be accessible through the regular query execution path, they are explicitly picked up by the schema generator based on their directives.
Resolving Federated Types
In traditional (i.e. non-federated) GraphQL servers, each of the output types are accessible through a traversal of the GraphQL schema from a corresponding query, mutation or subscription root type. Since federated GraphQL types might be accessed outside of the query path, we need a mechanism to access them in a consistent manner — federated GraphQL servers expose a new _entities
query specifically for this purpose.
In order to simplify integrations, graphql-kotlin-federation provides a default _entities
query resolver that relies on FederatedTypeRegistry to retrieve all FederatedTypeResolvers that resolve target objects. When configuring the federated schema generator hooks you have to explicitly provide those resolver mappings to the configuration.
Putting it all together
Base Schema
A base schema refers to the type defined in a GraphQL service that will be extended by other GraphQL services. In the example below, we define a base Product
type with id
and description
fields. id
is the primary key that uniquely identifies the Product
type and is specified in @KeyDirective
annotation. Since it is a base schema that doesn’t expose any extended functionality our FederatedTypeRegistry does not include any federated resolvers.
The example above generates the following schema with additional federated types:
Extended Schema
Extended federated GraphQL schemas provide additional functionality to the types already exposed by other GraphQL services. In the example below, Product
type from our base schema is extended with the new reviews
field. The primary key needed to instantiate the Product
type (i.e. id
) has to match the @key
definition on the base type. Since primary keys are defined on the base type and are only referenced from the extended type, all of the fields that are part of the fieldset specified in @key
directive have to be marked with @external
directive. Finally, we also need to specify an “entry point” for the federated type — we need to create a FederatedTypeResolver that will be used to instantiate the federated Product
type when processing federated queries.
Our extended schema will then be generated as:
Federated Schema
Once we have both base and extended GraphQL services up and running, we will also need to configure Federated Gateway to combine them into a single schema. Using the examples above, our final federated schema will be generated as:
See our federation example for additional details.
Further Reading
GraphQL Kotlin consists of a number of libraries that aim to simplify the creation of a GraphQL server in Kotlin. graphql-kotlin-federation is built on top of graphql-kotlin-schema-generator and allows developers to easily generate Federated GraphQL schemas directly from their code. Similar to regular GraphQL schemas, the federated schema generation process can also be customized by providing various hooks that fire during specific schema generation lifecycle events. For additional details on how to use the library please check out the examples and the documentation available on the GitHub pages. Stay tuned for additional blog posts where we’ll explore other parts of GraphQL-Kotlin libraries.
We are also open for feedback so please raise an issue and start a discussion for any new features you would like to see!
- https://medium.com/expedia-group-tech/creating-graphql-schemas-in-kotlin-aaaac0ab0672
- https://expediagroup.github.io/graphql-kotlin/
- https://www.apollographql.com/docs/apollo-server/federation/introduction/
Photo by Denys Nevozhai on Unsplash