Building a supergraph by stitching results
Build a supergraph (a graph of subgraphs) using the @materializer
directive to "stitch" together results from multiple backends.
Stitching results to build a supergraph uses Federation in API Connect for GraphQL to connect two or more GraphQL APIs into one unified GraphQL API.
Building a supergraph by stitching results involves the following steps:
- Build subgraphs against each backend. You can do that in one of two ways:
- Let API Connect for GraphQL introspect the
backend using
stepzen import
, or - Build your own subgraph using
@rest
,@dbquery
and@graphql
directives.
- Let API Connect for GraphQL introspect the
backend using
-
Combine the subgraphs using
@materializer
directive.
In this topic, we will focus on combining the subgraphs.
Combine subgraphs using the @materializer
directive
Suppose you have built up two subgraphs: one for Customer
(against a REST
backend), and another for Order
(against a MySQL database). At the simplest, your
schemas might look like:
customer.graphql
type Customer { name: String id: ID email: String } type Query { customer(id: ID): Customer @rest(endpoint: "https://api.acme.com/customers/$id") customerByEmail(email: String): Customer @rest(endpoint: "https://api.acme.com/customers?email=$email") }
order.graphql
type Order { createOn: Date amount: Float } type Query { orders(customerId: ID): [Order] @dbquery(type: "mysql", table: "orders") }
For building the supergraph, it does not matter how each subgraph is built. They are stitched together at the schema level, not at the implementation level. The stitching syntax is simple: data from one subgraph is used to provide arguments to a field selection in the same or different subgraph, which is then used to resolve a field in the originating subgraph through a series of transformations.
At its simplest, this might look like a new file called
customer-orders.graphql
:
extend type Customer {
orders: [Order]
@materializer (query: "orders", arguments: [{name: "customerId", field: "id"}]
}
Let's examine the query:
- The
extend
clause extends a subgraph with new fields. In this case, we are adding a new field totype Customer
. We can achieve the same purpose by editing thecustomer.graphql
file, but usingextend
keeps the concerns of the supergraph separate from those of the subgraphs, allowing each to evolve and be maintained with minimal impact to the other. - The new field declaration, which defines the name of the new field (
orders
), its type ([Order]
), and a @materializer directive. - The
@materializer
directive specifies a field selection that is used to resolve the new field. It has twoarguments
:- A field selection, specified by the
query
argument. This field selection must have the same type as the the annotated field. In this case, both have the type[Orders]
. The field selection need not be a top-level field selection. It can be a nested field selection, as long as the type of the field selection matches the type of the new field. For example, you could add acustomerName: String
field to a type, and populate it with a materializer like this:customerName: String @materializer(query: "customerByEmail { name }")
- A list of argument mappings, specified by the
arguments
argument. Basically, you are telling API Connect Essentials how to map the arguments and fields of the annotated field and its enclosing type, respectively, to the arguments of the field selection specified byquery
. So here, thequery: "orders"
gets thecustomerId
argument from the fieldid
ofCustomer
.
- A field selection, specified by the
The way in which orders
is implemented is irrelevant for the stitching. That is
the beauty of the API Connect for GraphQL
supergraph.
Sometimes, you have to perform a series of transformations in a specific sequence to massage the
data from the first subgraph so that it provides the correct arguments for the second subgraph. For
example, to extend customers
with weather
information, you might
have to geocode the field address
into lat,lon
before resolving
weather
. You can do that easily in API Connect Essentials using the
@sequence
construct:
extend type Customer {
weather: Weather @materializer(query: "customerWeather")
}
type Coord {
lat: Float
lon: Float
}
type Query {
customerWeather(address: String): Weather
@sequence(steps: [{ query: "geocode" }, { query: "weather" }])
geocode(address: String): Coord
# any implementation, using @rest, @graphql, @dbquery
}
The API Connect for GraphQL federation model
As you can see, the stitching (using @materializer
and, when needed,
@sequence
) is independent of how that subgraph is formed.
When all subgraphs are formed using @graphql
, we are federating across a set of
subgraphs. Whether each subgraph is built in API Connect for GraphQL or not, the subgraphs your
teams use can all be made into a federated supergraph within API Connect for GraphQL with
@materializer
and @sequence
. For more information on federation,
see Using GraphQL federation
to build a supergraph.