Link Types Using @materializer
Declaratively build a graph of graphs using the @materializer directive.
In StepZen, your GraphQL API against multiple backends is built by:
-
First building subgraphs against each backend. You can do that in one of two ways:
-
Let us introspect the backend using
stepzen import, or -
Build your own subgraph using
@rest,@dbqueryand@graphqldirectives.
-
-
Combining these subgraphs using
@materializerdirective.
In this section, we will focus on step 2 - combining the subgraphs.
Combine subgraphs using the @materializer directive
Let us say that 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 schema 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 customer-orders.graphql:
extend type Customer {
orders: [Order]
@materializer (query: "orders", arguments: [{name: "customerId", field: "id"}]
}
Let us examine what the preceding command is:
- The
extendclause 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.graphqlfile, but usingextendkeeps 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, that defines the name of the new field (
orders), its type ([Order]), and a @materializer directive. - The
@materializerdirective specifies a field selection that is used to resolve the annotated field. It has twoarguments:- A field selection, specified by the
queryargument. This field selection must have the same type as the the new field. In this case, both have the same 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: Stringfield 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
argumentsargument. Basically, you are telling StepZen 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 thecustomerIdargument from the fieldidofCustomer.
- A field selection, specified by the
As you can see in the preceding section, the way in which orders is implemented is irrelevant for the stitching. That is the beauty of the StepZen graph of subgraphs concepts. You can stitch a layer.
Sometimes, you have to do a series of transformations to massage the data from the first subgraph to provide the right 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 StepZen 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 StepZen 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 StepZen or not, the subgraphs your teams use can all be made into a federated supergraph in StepZen with
@materializer and @sequence.