Type merging

Consolidate the schema definitions, including overlapping type definitons, and resolvers from multiple subschema instances into a stitched schema.

When you merge multiple API subschema into one stitched schema, you might have one type that is defined in multiple subschemas. To ensure that only one reconciled type exists for the stitched schema, and that it contains all the necessary fields across each subschema, you use type merging.

To enable type merging, you create a Gloo GraphQLStitchedSchema custom resource that references each GraphQL API subschema. Within the subschemas that contain a shared type, you specify the type name, and the set of partial fields that the subschema provides. Gloo Gateway uses this resource to generate a stitched schema, which incorporates all the partial types from each of the respective services. When clients query the route for the stitched schema, Gloo Gateway creates requests to the individual services, and then stitches the responses back together into one response to the client. For more information, see What is GraphQL stitching?.

Overlapping type example

To understand overlapping schema types, suppose that you want to merge the subschema for two APIs: the users service and the accounts service.

In the users service ApiDoc, you might define the following GetUser query to retrieve a user by a query-supplied username. The User type defines the username, firstname, and lastname fields.

...
type Query {
  GetUser(username: String): User
}
type User {
  username: String
  firstName: String
  lastName: String
}

In the accounts service ApiDoc, you also have a User type. Unlike the users service, however, the accounts service's User type is partial, and has only the username field.

...
type Account {
  owner: User
}
type User {
  username: String
}

To ensure that the accounts subschema can provide its partial User type (with only the username) and have access to the full set of User type fields, you provide the relevant information from the type as it is fully defined in the users subschema. In the reference for the users subschema in your GraphQLStitchedSchema CR, you provide the following typeMerge section:

...
subschemas:
  - schema:
      name: users-subschema
      namespace: customers
      clusterName: cluster1
    typeMerge:
      User:
          selection_set: '{ username }'
          query_name: 'GetUser'
          args:
          username: username
  - schema:
      name: accounts-subschema
      ...

The resultant stitched schema can now provide the full User type definition to all partial User types in other APIs that require it. In this case, you can now retrieve, for example, the first name of a user from the Account.owner field, even though the accounts schema doesn't provide the first name from the User type in its own ApiDoc CR.

Merge GraphQL schema and types

For each API, define schema and select resolver services in your Gloo environment. Then, you can merge the GraphQL APIs together and merge overlapping types in a GraphQLStitchedSchema, and set up routing to this merged resolver. Note: Type merging is not supported for mutation and subscription types.

The examples in this guide are based on the Bookinfo sample app. To check out individual resources you might create for a stitched Bookinfo schema that uses type merging, check out the Gloo custom resources in the Gloo sample repository.

  1. For each subschema, use the following guides to create individual CRs for each API that define its schema and resolvers.

    1. Define subschema in separate ApiDoc CRs. For the Bookinfo example, you might define three CRs for the product, reviews, and ratings services.
    2. Choose how GraphQL requests are executed, and follow the guides to either proxy requests to existing GraphQL backends or define REST, gRPC, or mock resolvers for each schema.
  2. Create one GraphQLStitchedSchema CR that references each GraphQL subschema. For each overlapping type, specify the partial fields that the type provides in each subschema. For example, in this stitched schema for Bookinfo, the schema references for ratings and reviews specify the id type that each subschema contains. For each typeMerge, the partial fields that each subschema provides for the id type are specified in the selectionSet.

    apiVersion: apimanagement.gloo.solo.io/v2
    kind: GraphQLStitchedSchema
    metadata:
      name: stitched-graphql-schema
      namespace: bookinfo
    spec:
      subschemas:
      - schema:
          clusterName: $CLUSTER_NAME
          name: products-graphql-schema
          namespace: bookinfo
      - schema:
          clusterName: $CLUSTER_NAME
          name: ratings-graphql-schema
          namespace: bookinfo
        typeMerge:
          Ratings:
            args:
              id: id
            queryName: GetRatings
            selectionSet: '{ id }'
      - schema:
          clusterName: $CLUSTER_NAME
          name: reviews-graphql-schema
          namespace: bookinfo
        typeMerge:
          Reviews:
            args:
              id: id
            queryName: GetReviews
            selectionSet: '{ id }'
    
  3. Create a route table that routes to a graphql.stitchedSchema destination. Specify your stitched schema resoure name in the graphql.stitchedSchema.name field, such as stitched-graphql-schema in the following examples.

    To route requests from clients that are external to your Gloo Gateway setup, you can use the following example route table.

    apiVersion: networking.gloo.solo.io/v2
    kind: RouteTable
    metadata:
      name: graphql-bookinfo
      namespace: bookinfo
    spec:
      hosts:
      - www.example.com
      http:
      - name: graphql-bookinfo
        labels: 
          route: graphql-bookinfo
          graphql: "true"
        graphql:
          stitchedSchema:
            clusterName: $CLUSTER_NAME
            name: stitched-graphql-schema
            namespace: bookinfo
        matchers:
        - uri:
            prefix: /graphql
      virtualGateways:
      - name: istio-ingressgateway
    

    To route requests from clients that are internal to your setup, such as if you use Gloo Mesh, you can use the following example route table. Note that because the GraphQL destination references the stitched schema resource instead of a service, the value of hosts is arbitrary, as long as the request from the client app matches the host.

    apiVersion: networking.gloo.solo.io/v2
    kind: RouteTable
    metadata:
      name: graphql-bookinfo
      namespace: bookinfo
    spec:
      hosts:
      - graphql-bookinfo.internal.com
      workloadSelectors:
      - selector:
          labels:
            app: client-app
      http:
      - name: graphql-bookinfo
        labels: 
          route: graphql-bookinfo
          graphql: "true"
        graphql:
          stitchedSchema:
            clusterName: $CLUSTER_NAME
            name: stitched-graphql-schema
            namespace: bookinfo
        matchers:
        - uri:
            prefix: /graphql
    

  4. Test routing to your stitched resolver setup.

    1. Get the external IP address of your ingress gateway.
      • GKE LoadBalancer IP address:
        export INGRESS_GW_IP=$(kubectl get svc -n gloo-mesh-gateways istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
        echo $INGRESS_GW_IP
        
      • EKS LoadBalancer hostname:
        export INGRESS_GW_IP=$(kubectl get svc -n gloo-mesh-gateways istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
        echo $INGRESS_GW_IP
        
    2. Send a request with a query to the GraphQL endpoint to verify that the request is successfully resolved by the gateway. This example command sends the request to the /graphql path and includes a query for the Bookinfo example.
      curl -vik --resolve www.example.com:443:${INGRESS_GW_IP} https://www.example.com:443/graphql \
      -d '{"query":"query { productsForHome { title ratings {reviewer numStars}} }"}'
      

    To test routing to the server, choose one of the following options:

    • Log in to your client app and send a query to the host and path to verify that the GraphQL server is reachable through the route. For the example route table, you might use this curl command:
      curl http://graphql-bookinfo.internal.com/graphql -d '{"query":"query { productsForHome { title ratings {reviewer numStars}} }"}'
      
    • Create a temporary curl pod in the bookinfo namespace and send a request to the GraphQL server from your local machine.
      1. Create the curl pod.
        kubectl run -it -n bookinfo curl --image=curlimages/curl:7.73.0 --rm  -- sh
        
      2. Send a request with a query to the GraphQL endpoint to verify that the request is successfully resolved by the gateway. This example command sends the request to the /graphql path and includes a query for the Bookinfo example.
        curl http://graphql-bookinfo.internal.com/graphql -d '{"query":"query { productsForHome { title ratings {reviewer numStars}} }"}'
        
      3. Exit the temporary pod. The pod deletes itself.
        exit
        

Reference

For more information, see the Gloo Gateway API reference for the GraphQLStitchedSchema CR.