Schema stitching
When you use GraphQL in Gloo Gateway, you can stitch multiple schemas together to expose one unified GraphQL server to your clients.
Consider a cluster that has two existing GraphQL APIs, user-svc
and product-svc
. Each service has similar information that you might want to provide as part of a unified data model. Typically, clients must stitch together the services in the frontend. With Gloo Gateway, you can instead stitch the GraphQL schemas for these services together in the backend, and expose a unified GraphQL server to your clients. This stitching frees your clients to consider only what data that they want to fetch, not how to fetch the data.
Follow along with the user and product service example.
Reviewing each service’s configuration
To understand how stitching occurs, consider the data model and example queries for both services, starting with the user service.
User service: The GraphQLApi
resource for the user service defines a partial type definition for the User
type, and a query for how to get the full name of a user given the username.
apiVersion: graphql.gloo.solo.io/v1beta1
kind: GraphQLApi
metadata:
name: user-graphql
namespace: product-app
spec:
executableSchema:
executor:
local:
enableIntrospection: true
resolutions:
Query|UserService.GetUser:
grpcResolver:
requestTransform:
methodName: GetUser
outgoingMessageJson:
username: '{$args.username}'
serviceName: user.UserService
upstreamRef:
name: user-svc
namespace: product-app
schemaDefinition: |
type User {
username: String
fullName: String
userId: Int
}
type Query {
GetUser(username: String): User @resolve(name: "Query|UserService.GetUser")
}
query {
GetUser(username: "akeith") {
fullName
userId
}
}
{
"GetUser": {
"fullName": "Abigail Keith"
"userId": 346
}
}
Product service: The GraphQLApi
resource for the product service also defines a partial type definition for the User
type, and a query for how to get the product name and the seller’s username given the product ID.
apiVersion: graphql.gloo.solo.io/v1beta1
kind: GraphQLApi
metadata:
name: product-graphql
namespace: product-app
spec:
executableSchema:
executor:
local:
enableIntrospection: true
resolutions:
Query|ProductService.GetProduct:
grpcResolver:
requestTransform:
methodName: GetProduct
outgoingMessageJson:
id: '{$args.id}'
name: '{$args.name}'
serviceName: product.ProductService
upstreamRef:
name: product-svc
namespace: product-app
schemaDefinition: |
type User {
username: String
}
type Product{
id: Int
name: String
seller: User
}
type Query {
GetProduct(id: Int): Product @resolve(name: "Query|ProductService.GetProduct")
}
query {
GetProduct(id: 125) {
name
seller {
userId
}
}
}
{
"GetProduct": {
"name": "Narnia",
"seller": {
"userId": 346
}
}
}
What if a client wants the full name of the seller for a product, instead of the username? Given the product ID, the client cannot get the seller’s full name from the product service. However, the full name of any user is provided by the user service.
Stitching together the services
When you have different services with data that you want clients to be able to request, you can stitch the services together. In a separate GraphQLApi
resource, specify a stitchedSchema
section that indicates how to merge the types between the services.
In the user service subschema, you can specify which fields are unique to the User
type, and how to get these fields. For example, in the following typeMerge
, Gloo Gateway can use the GetUser
query to provide the full name from the user service.
apiVersion: graphql.gloo.solo.io/v1beta1
kind: GraphQLApi
metadata:
name: stitched-graphql
namespace: product-app
spec:
stitchedSchema:
subschemas:
- name: user-svc
namespace: product-app
typeMerge:
User:
selectionSet: '{ username }'
queryName: GetUser
args:
username: username
- name: product-svc
namespace: product-app
As a result, Gloo Gateway generates a stitched service. From this one stitched service, a client can provide the product ID, and recieve the product name, the username of the seller, the user ID of the seller, and the full name of the seller.
type User {
username: String
fullName: String
userId: Int
}
type Product{
id: Int
name: String
seller: User
}
type Query {
GetUser(username: String): User @resolve(name: "Query|UserService.GetUser")
GetProduct(id: Int): Product @resolve(name: "Query|ProductService.GetProduct")
}
Querying the stitched service
Based on the stitched service, Gloo Gateway generates the following schema definition, which incorporates all the types and queries from each of the respective services.
Clients can query the stitched service. In the background, Gloo Gateway uses this schema to create the requests to the stitched service, and then stitches the responses back together into one response to the client.
query {
GetProduct(id: 125) {
name
seller {
username
fullName
userId
}
}
}
{
"GetProduct": {
"name": "Narnia",
"seller": {
"username": "akeith"
"fullName": "Abigail Keith"
"userId": 346
}
}
}
Next steps
For more information, check out the API reference documentation for stitchedSchema
.