GraphQL Federated Schemas Introspection With Mercurius

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.

JSON
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.

JSON
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.

JSON
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  is a simple Mercurius plugin that exposes a custom API that returns every service and details about its subgraph.

JSON
{    
	"Service 1": {        
		"__schema" : { ... ServiceSchema }    
	},    
	"Service 2": {        
		"__schema" : { ... ServiceSchema }    
	},    
}

The 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

JavaScript
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 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.

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:

JavaScript
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 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 it helps with the common issues that arise when using graphql in production.

References

Insight, imagination and expertly engineered solutions to accelerate and sustain progress.

Contact