What is GraphQL federation?
With regards to GraphQL Federated schemas, Apollo federation v1 is one of the most well-known methods of implementing GraphQL in microservices.
The idea is to divide the data graph among multiple federation-compliant services. Then a supergraph schema is composed according to the Apollo federation specification. A client can then query the gateway and access all subgraphs, and the gateway routes the query to the correct services.
The main advantage of a federated architecture is having one single graph that clients can query. However, each subgraph remains independent and flexible enough to be defined, developed, and maintained by separate teams and have its own release cycle. The composed supergraph does not contain information about the subschemas. There is no way to find out from the composed schema where certain types are defined or where fields are extended with new fields. For production deployment that might be ok, but it makes it hard to debug.
Federated schemas in real life
As the supergraph grows, finding which team handles what part of the graph becomes harder. The consumers of the supergraph might need to quickly figure out which team to contact when a bug is found or additional data is required. Mercurius by itself does not expose this information, so from the client perspective the information about federation is completely hidden.
A simple federated scenario
In this scenario we define 2 subgraph schemas. User service is in charge resolving the User entity, Post service resolves the Post entities, but also enhances the User entity with list of posts.
User service schema
This service would be the one that handles the users.
extend type Query {
# Defines a new query that will return a list of users
findUser(name: String): [User]
}
# Defines the user type
type User @key(fields: "id") {
id: ID!
name: String!
fullName: String
friends: [User]
}
Post service schema
This service would be the one that handles the blog posts.
type Query {
# Defines a new query that will return a the list of Posts
topPosts(count: Int): [Post]
}
# Defines a new type, that references Users through "author"
type Post {
pid: ID!
title: String
content: String
author: User @requires(fields: "pid title")
}
# This is a reference to the User entitty, where the reference key is "id"
type User @key(fields: "id") @extends {
# @external fields come from the User service
id: ID! @external
name: String @external
# once merged this will become part of the federated User entity
posts: [Post]
}
Gateway supergraph schema
The one graph, where user service schema and post service schema were combined automatically by mercurius following the federation specification.
type Query {
findUser(name: String): [User]
topPosts(count: Int): [Post]
}
type User {
id: ID!
name: String!
fullName: String
friends: [User]
posts: [Post]
}
type Post {
pid: ID!
title: String
content: String
author: User`
}
The composed supergraph schema does not contain information from which service parts of the supergraph are coming from. That is correct for production environment, but makes debugging hard.
Federation info Mercurius plugin
[mercurius-federation-info](https://github.com/nearform/mercurius-federation-info)
is a simple Mercurius plugin that exposes a custom API that returns every service and details about its subgraph.
{
"Service 1": {
"__schema" : { ... ServiceSchema }
},
"Service 2": {
"__schema" : { ... ServiceSchema }
},
}
The [mercurius-federation-info-graphiql-plugin](https://github.com/nearform/mercurius-federation-info-graphiql-plugin)
then uses this information to show how the supergraph is constructed.
For the basic usage of the plugin there is no need to install the mercurius-federation-info-graphiql-plugin
separately, as it is already included in mercurius-federation-info
.
Adding the plugin to Mercurius
import Fastify from 'fastify'
import mercurius from 'mercurius'
import mercuriusFederationInfo, { federationInfoGraphiQLPlugin } from 'mercurius-federation-info'
//define mercurius gateway
const app = Fastify({ logger: true })
app.register(mercurius, { schema })
app.register(mercuriusFederationInfo, {})
app.register(mercurius, {
gateway,
graphiql: {
plugins: [federationInfoGraphiQLPlugin()]
}
})
Federation info GraphiQL plugin
[mercurius-federation-info-graphiql-plugin](https://github.com/nearform/mercurius-federation-info-graphiql-plugin)
is the client side of the plugin that adds two views to the GraphiQL interface (already included in mercurius). The views show the details of how the unified schema constructed with schema federation.
Unified Schema Panel
A set of tables displaying information about the unified schema, where we can quickly find which services define or reference various parts of the one graph.
There are currently four main groups: Queries, Mutations, Subscriptions and Types.
When expanded, Queries, Mutations and Subscriptions list every root field (combined from all services). The columns show the information about each field and in which service it is defined.
The Types table list all object types defined across all the federated services.
Services Panel
The Services panel lists all the services that are being federated. When a service is expanded we can see a tree view of all the types that are defined by that service.
The plugin is also available as a standalone GraphiQL plugin
Restricting access to subgraph details
The plugin can also be used in production as it allows fine-tuned access control.
The enabled
option also accepts a function, which can be used to conditionally enable the plugin:
app.register(mercuriusFederationInfo, {
enabled: ({ schema, source, context }) => {
return context.user.isAdmin
}
})
In this way, only requests sent by an hypothetical admin user will have the plugin enabled and contain the detailed subgraph schema.
Conclusions
GraphQL federation on mercurius is a great way to combine multiple subgraphs into one single supergraph and [mercurius-federation-info](https://github.com/nearform/mercurius-federation-info)
makes it is easier to have a detailed understanding how the unified graph is constructed.
It can aid in communication with other teams, bug reporting and bug discovery. Together with [mercurius-explain](https://www.nearform.com/blog/graphql-performance-mercurius-explain/)
it helps with the common issues that arise when using graphql in production.