The GraphQL API Composer is a framework agnostic library for combining multiple GraphQL APIs, known as subgraphs, into a single API capable of querying data across any of its constituent subgraphs.
The Composer downloads each subgraph schema from the corresponding upstream servers. The subgraphs will be merged to create a supergraph API. The combined API exposes the original operations from each subgraph. Furthermore, types with the same name in multiple subgraphs are merged together into entities.
Given the following Books
subgraph schema:
enum BookGenre {
FICTION
NONFICTION
}
type Book {
id: ID!
title: String
genre: BookGenre
}
type Query {
getBook(id: ID!): Book
getBooks(ids: [ID]!): [Book]!
}
And the following Reviews
subgraph schema:
input ReviewInput {
bookId: ID!
rating: Int!
content: String!
}
type Review {
id: ID!
rating: Int!
content: String!
}
type Author {
id: ID!
}
type Book {
id: ID!
reviews: [Review]!
author: Author
}
type ReviewWithBook {
id: ID!
rating: Int!
content: String!
book: Book!
}
type Query {
getReview(id: ID!): Review
getReviews(ids: [ID]!): Review
getReviewBook(bookId: ID!): Book
}
type Mutation {
createReview(review: ReviewInput!): Review!
}
When the Books
and Reviews
subgraphs are merged, the Book
type becomes:
TODO
type Book {
# Common field used to uniquely identify a Book.
id: ID!
# Fields from the Books subgraph.
title: String
genre: BookGenre
# Fields from the Reviews subgraph.
rate: Int
reviews: [Review]!
}
The following example shows how the GraphQL API Composer can be used with Fastify and Mercurius - see /examples/getting-started.js for the full code.
'use strict'
const { compose } = require('@platformatic/graphql-composer')
const Fastify = require('fastify')
const Mercurius = require('mercurius')
async function main () {
// Get schema information from subgraphs
const composer = await compose({
subgraphs: [
{
// Books subgraph information
name: 'books',
// Subgraph server to connect to
server: {
host: booksServiceHost
},
// Configuration for working with Book entities in this subgraph
entities: {
Book: {
pkey: 'id',
// Resolver for retrieving multiple Books
resolver: {
name: 'getBooks',
argsAdapter: (partialResults) => ({
ids: partialResults.map(r => r.id)
})
}
}
}
},
{
// Reviews subgraph information
name: 'reviews',
server: {
host: reviewsServiceHost
},
// Configuration for Review entity
entities: {
Review: {
pkey: 'id',
// Resolver for retrieving multiple Books
resolver: {
name: 'getReviews',
argsAdapter: (partialResults) => ({
ids: partialResults.map(r => r.id)
})
}
},
// Book entity is here too
Book: {
pkey: 'id',
// Resolver for retrieving multiple Books
resolver: {
name: 'getReviewBooks',
argsAdapter: (partialResults) => ({
bookIds: partialResults.map(r => r.id)
})
}
}
}
}
]
})
// Create a Fastify server that uses the Mercurius GraphQL plugin
const composerService = Fastify()
composerService.register(Mercurius, {
schema: composer.toSdl(),
resolvers: composer.resolvers,
graphiql: true
})
await composerService.ready()
await composerService.listen()
// Then query the composer service
// { getBook (id: 1) { id title reviews { content rating } } }
}
main()
-
Arguments
config
(object, optional) - A configuration object with the following schema.defaultArgsAdapter
(function, optional) - The defaultargsAdapter
function for the entities.addEntitiesResolvers
(boolean, optional) - automatically add entities types and resolvers accordingly with configuration, see composer entities section.logger
(pino instance, optional) - The composer loggersubgraphs
(array, optional) - Array of subgraph configuration objects with the following schema.name
(string, optional) - A unique name to identify the subgraph; if missing the default one is#${index}
, where index is the subgraph index in the array.server
(object, required) - Configuration object for communicating with the subgraph server with the following schema:host
(string, required) - The host information to connect to.composeEndpoint
(string, optional) - The endpoint to retrieve the introspection query from. Default:'/.well-known/graphql-composition'
. In case the endpoint is not available, a second call with introspection query will be sent to thegraphqlEndpoint
.graphqlEndpoint
(string, optional) - The endpoint to make GraphQL queries against. Default:'/graphql'
.
entities
(object, optional) - Configuration object for working with entities in this subgraph, the values are objects with the the following schema:resolver
(object, optional) - The resolver to retrieve a list of objects - should return a list - and should accept as a arguments a list of primary keys or foreign keys.name
(string, required) - The name of the resolver.argsAdapter (partialResults)
(function, optional) - This function is invoked with a subset of the result of the inital query;partialResults
is an array of the parent node. It should return an object to be used as argument forresolver
query. Default: if missing, thedefaultArgsAdapter
function will be used; if that is missing too, a generic one will be used.partialResults
(function, optional) - The function to adapt the subset of the result to be passed toargsAdapter
- usually is needed only on resolvers offkeys
andmany
.
pkey
(string, required) - The primary key field to identify the entity.fkeys
(array of objects, optional) an array to describe the foreign keys of the entities, for examplefkeys: [{ type: 'Author', field: 'authorId' }]
.type
(string, required) - The entity type the foreign key is referrered to.field
(string, optional) - The foreign key field.as
(string, optional) - When usingaddEntitiesResolvers
, it defines the name of the foreign entity as a field of the current one, as a single type.pkey
(string, optional) - The primary key of the foreign entity.subgraph
(string, optional) - The subgraph name of the foreign entity, where the resolver is located; if missing is intended the self.resolver
(object, optional) - The resolver definition to query the foreing entity, same structure asentity.resolver
.
many
(array of objects, optional) - Describe a 1-to-many relation - the reverse of the foreign key.type
(string, required) - The entity type where the entity is a foreign key.fkey
(string, optional) - The foreign key field in the referred entity.as
(string, required) - When usingaddEntitiesResolvers
, it defines the name of the relation as a field of the current one, as a list.pkey
(string, optional) - The primary key of the referred entity.subgraph
(string, optional) - The subgraph name of the referred entity, where the resolver is located; if missing is intended the self.resolver
(object, required) - The resolver definition to query the referred entity, same structure asentity.resolver
.
onSubgraphError
(function, optional) - Hook called when an error occurs getting schema from a subgraph. The default function will throw the error. The arguments are:error
(error) - The error.subgraph
(string) - The erroring subgraph name.
queryTypeName
(string, optional) - The name of theQuery
type in the composed schema. Default:'Query'
.mutationTypeName
(string, optional) - The name of theMutation
type in the composed schema. Default:'Mutation'
.
-
Returns
- A
Promise
that resolves with aComposer
instance.
- A
When this function is called, all of the subgraph schemas are retrieved and merged into a supergraph. A Promise
resolving to a Composer
instance is returned.
Instances of the Composer
class are returned from the compose()
function. The Composer
constructor is not exposed as part of the public API.
- Arguments
- None
- Returns
SDL
string
Returns the SDL of the supergraph as a string; this can be passed to the GraphQL service as a schema definition.
- Arguments
- None
- Returns
IntrospectionQuery
object
Returns the supergraph schema as a GraphQL IntrospectionQuery
object. This representation can be passed to other functions such as GraphQL's buildClientSchema()
function.
An object containing the GraphQL resolver information for the supergraph.