GraphQL allows you to build a unified API across multiple data sources that can be queried with a single request from the client. Initially this was mainly used to expose a single GraphQL API that spanned multiple REST sources but as GraphQL picked up in popularity the need to expose a GraphQL API that spanned multiple downstream GraphQL services became more common.
And so, schema stitching was born! GraphQL schema stitching lets you merge multiple GraphQL APIs into a single schema without re-implementing all the existing resolver logic.
In practice it might look like this:
An Author service implementing this schema
type Author {
id: ID!
name: String!
}
type Query {
author(id: ID!): Author
}
and a Book service implementing this schema
type Book {
id: ID!
title: String!
authorId: ID!
description: String
}
type Query {
book(id: ID!): Book
booksByAuthorId(id: ID!): [Book!]!
}
can be combined in a GraphQL gateway service that implements this schema
extend type Author {
books: [Book!]!
}
extend type Book {
author: Author!
}
to give the final schema
type Book {
id: ID!
title: String!
author: Author!
description: String
}
type Author {
id: ID!
name: String!
books: [Book!]!
}
type Query {
author(id: ID!): Author
book(id: ID!): Book
}
This lets you query for a given Author and all their Books in one query, or fetch all books written by the same author as a given book. This is neat, but it would normally require implementing mapping resolvers in the GraphQL gateway that know how to go from Author id to list of Book and from a Book to an Author. It's unfortunate that the Gateway service needs to have knowledge of either of these entities or their relationships when the only concern it should need to handle is schema stitching.
Thankfully, GraphQL has a very active community that has contributed a lot of great libraries. Building on top of the great work of the Apollo team with graphql-tools I built merge-remote-graphql-schemas, a JS library for automatically stitching GraphQL APIs together without the need to implement mapping resolvers in the stitching service.
Taking our example above, our Author service would just need to expose the same schema as before
type Author {
id: ID!
name: String!
}
type Query {
author(id: ID!): Author
}
and the Book service would expose this slightly different schema
type Book {
id: ID!
title: String!
author: Author!
description: String
}
type Author {
id: ID!
books: [Book!]!
}
type Query {
book(id: ID!): Book
author(id: ID!): Author
}
Same as in the previous example, the Book service owns the relationships between Books and Authors, but now it expresses them in a more idiomatic way.
merge-remote-graphql-schemas takes the GraphQL schemas exposed by these services and merges them (including merging the types) into
type Book {
id: ID!
title: String!
author: Author!
description: String
}
type Author {
id: ID!
name: String!
books: [Book!]!
}
type Query {
author(id: ID!): Author
book(id: ID!): Book
}
Under the hood it creates an executable GraphQL schema that automatically transforms incoming queries into sub-queries that each service knows how to resolve, then merges those results back into a single result. No mapping resolvers or type definitions need to be added to the stitching service, saving development effort and allowing us to keep our concerns nicely separated.
If you're interesting in schema stitching check out the GitHub repo and let me know what you think!
Top comments (0)