Overview

In this getting started tutorial, you define schema fields for the Bookinfo sample app services, define GraphQL resolvers for each field, and map the schema definitions to the resolvers. Then, you set up routes to the GraphQL server and test the routes by sending GraphQL queries.

The following diagram shows how each of the Gloo custom resources that you create in this guide function together for your GraphQL setup.

  • The routes in your RouteTable serve as the entry point which users can query.
  • When Gloo Mesh Gateway receives a query to your GraphQL route, it matches the request to a particular GraphQLSchema, which contains the mappings of GraphQL resolvers to API schema fields.
    • GraphQL resolvers are defined in a GraphQLResolverMap. Resolvers return results from the backing service for the API schema field that the resolver is mapped to.
    • API schema fields are defined in an ApiDoc. API schema fields determine what kind of data can be returned to a client that makes a GraphQL query to your endpoint.
  • Any GraphQL policies you apply, such as to allow only specific queries or to cache queries, are applied when Gloo Mesh Gateway receives and processes the request.

Figure: Gloo custom resources for a GraphQL API
Figure: Gloo custom resources for a GraphQL API

Install GraphQL

  1. Set the Gloo Mesh Gateway license with the Gloo GraphQL add-on as an environment variable.

      export GLOO_MESH_GATEWAY_LICENSE_KEY=<key>
      
    • To check that your Gloo Mesh Gateway license includes the Gloo GraphQL add-on:
        meshctl license check --key $(echo ${GLOO_MESH_GATEWAY_LICENSE_KEY} | base64 -w0)
        
      In the output, verify that your Gloo Mesh Gateway license is current and valid, and a GraphQL module is added to the license.
        INFO  License key gloo-gateway-license-key for product gloo-gateway is valid. Expires at 08 Oct 24 12:31 CEST
      INFO  License key gloo-gateway-license-key for gloo-gateway/graphql add-on is valid. Expires at 08 Oct 24 12:31 CEST
      SUCCESS  Licenses are valid
        
    • To purchase a license, contact an account representative.
  2. Install GraphQL by installing Gloo Mesh Gateway for the first time, or by upgrading your existing Gloo Mesh Gateway installation.

  3. Deploy the Bookinfo sample app and configure an HTTP or HTTPS listener on your ingress gateway.

Set up GraphQL resolvers with Bookinfo

Explore GraphQL resolution with the Bookinfo sample application.

In Gloo Mesh Gateway, you can create GraphQL REST and gRPC resolvers to fetch the data from your backend. In the following steps, you define schema fields for Bookinfo services, define GraphQL resolvers for each field, and map the schema definitions to the resolvers.

  1. If you have not already, save the name of your cluster in an environment variable.

      export CLUSTER_NAME=<cluster>
      
  2. Start by defining your schema in an ApiDoc Gloo custom resource, which determines what kind of data can be returned to a client that makes a GraphQL query to your endpoint. In this schema definition:

    • The top-level query type defines the productsForHome field. This field references the Product object type, which means that when a client queries productsForHome, fields from Product are returned.
    • The Product object type defines the id, title, descriptionHtml, reviews, and ratings fields. The reviews and ratings fields reference object types of their own, which contain additional fields.
      kubectl apply -f - <<EOF
    apiVersion: apimanagement.gloo.solo.io/v2
    kind: ApiDoc
    metadata:
      name: bookinfo-rest-apidoc
      namespace: bookinfo
    spec:
      graphql:
        schemaDefinition: |-
         # Top-level query type
          type Query {
              """Description of a book in HTML"""
              productsForHome: [Product]
            }
            # Product object type
            """Each book has a product entry"""
            type Product {
              """Unique identifier for books"""
              id: String
              """The book title"""
              title: String
              """Description of a book in HTML"""
              descriptionHtml: String
              """List of reader reviews for this book. Queries the reviews REST service"""
              reviews: [Review] 
              """List of reader ratings for this book. Queries the ratings REST service"""
              ratings: [Rating] 
            }
            """A book review"""
            type Review {
                """Name of the reviewer"""
                reviewer: String
                """Review details"""
                text: String
                "Reviewer Rating, this field is provided by the reviews REST service, which queries the ratings REST service"
                rating: ReviewerRating
            }
            type ReviewerRating {
              stars: Int
              color: String
            }
            """A book rating"""
            type Rating {
                """Name of the user peforming the rating"""
                reviewer: String
                """Number of stars for this rating"""
                numStars: Int
            }
    EOF
      
  3. Define the resolvers for the schema fields in a GraphQLResolverMap Gloo custom resource. A resolver is responsible for returning results from the backing service for the field that the resolver is mapped to. In this resolver map:

    • For the productsForHome field in the top-level query type, the productpage Bookinfo service is defined as the resolver. In other words, when a client queries productsForHome, the productpage service resolves this request and fetches information from the Product type.
    • For the Product object type, the ratings and reviews Bookinfo services are defined as resolvers for the ratings and reviews fields. In other words, when the productpage service fetches information about the Product type, the ratings and reviews services return information for the product’s ratings and reviews fields.
    • For ratings and reviews, a resolverResultTransform jq filter is applied to the results for each.
      • The result data from ratings is not formatted in a way that the client can understand, so this jq filter reformats the result data so that it is returned in sets of entries for each reviewer and their rating. For example, the response is transformed such as: {"reviewer":"Reviewer1","numStars":5},{"reviewer":"Reviewer2","numStars":4}.
      • The result data from reviews is typically formatted as {"id": 0, "reviews": { /*review content */}}. This jq filter reformats the result data to only return the reviews field content, without the id.
      kubectl apply -f - <<EOF
    apiVersion: apimanagement.gloo.solo.io/v2
    kind: GraphQLResolverMap
    metadata:
      name: bookinfo-rest-resolvermap
      namespace: bookinfo
    spec:
      types:
        # Top-level query type
        Query:
          fields:
            productsForHome:
              resolvers:
              - restResolver:
                  destinations:
                  - port:
                      number: 9080
                    ref:
                      name: productpage
                      namespace: bookinfo
                      cluster: $CLUSTER_NAME
                  request:
                    headers:
                      :path:
                        jq: '"/api/v1/products"'
        # Product object type
        Product:
          fields:
            ratings:
              resolvers:
              - resolverResultTransform:
                  jq: '.resolverResultVar.ratings | to_entries | map(.reviewer=.key | .numStars=.value | del(.key,.value))'
                restResolver:
                  destinations:
                  - port:
                      number: 9080
                    ref:
                      name: ratings
                      namespace: bookinfo
                      cluster: $CLUSTER_NAME
                  request:
                    headers:
                      :path:
                        jq: '"/ratings/" + (.parentVar.id | tostring)'
              variables:
                parentVar:
                  graphqlParent: {}
                resolverResultVar:
                  resolverResult: {}
            reviews:
              resolvers:
              - resolverResultTransform:
                  jq: '.resolverResultVar.reviews'
                restResolver:
                  destinations:
                  - port:
                      number: 9080
                    ref:
                      name: reviews
                      namespace: bookinfo
                      cluster: $CLUSTER_NAME
                  request:
                    headers:
                      :path:
                        jq: '"/reviews/" + (.parentVar.id | tostring)'
              variables:
                parentVar:
                  graphqlParent: {}
                resolverResultVar:
                  resolverResult: {}
    EOF
      
  4. Create a GraphQLSchema Gloo CR to map the types and fields from your schema definition that you defined in step 1 to the resolvers that you defined in step 2. The GraphQLSchema CR ensures that the GraphQL resolver services can access the field information for each type.

      kubectl apply -f - <<EOF
    apiVersion: apimanagement.gloo.solo.io/v2
    kind: GraphQLSchema
    metadata:
      name: bookinfo-rest-graphqlschema
      namespace: bookinfo
    spec:
      resolved:
        options: {}
        resolverMapRefs:
        - name: bookinfo-rest-resolvermap
          namespace: bookinfo
          clusterName: $CLUSTER_NAME
      schemaRef:
        name: bookinfo-rest-apidoc
        namespace: bookinfo
        clusterName: $CLUSTER_NAME
    EOF
      

Route to the GraphQL server

Set up routes to the GraphQL server and test the routes by sending GraphQL queries.

  1. Create a RouteTable resource with a graphql-bookinfo destination. This route table ensures that all GraphQL queries to the /graphql path are now handled by the GraphQL server in the gateway, which uses the resolvers and schema you referenced in your GraphQLSchema.

      kubectl apply -f - <<EOF
    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:
          schema:
            name: bookinfo-rest-graphqlschema
            namespace: bookinfo
            clusterName: $CLUSTER_NAME
        matchers:
        - uri:
            prefix: /graphql
      virtualGateways:
      - name: istio-ingressgateway
        namespace: bookinfo
    EOF
      
  2. Get the external address of your ingress gateway to test the /graphql route.

      export INGRESS_GW_ADDRESS=$(kubectl get svc -n gloo-mesh-gateways istio-ingressgateway -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}")
    echo $INGRESS_GW_ADDRESS
      
  3. Send a request to the GraphQL endpoint to verify that the request is successfully resolved by the gateway.

    In the JSON response, note that only the information you queried is returned:

      {"data":{"productsForHome":[{"title":"The Comedy of Errors","ratings":[{"reviewer":"Reviewer1","numStars":5},{"reviewer":"Reviewer2","numStars":4}]}]}}
      

Next steps

Now that you’ve tried out GraphQL with Gloo Mesh Gateway, check out the following pages to configure your own services for GraphQL integration.

Cleanup

You can optionally remove the resources that you set up as part of this guide.

  kubectl delete ApiDoc -n bookinfo bookinfo-rest-apidoc
kubectl delete GraphQLResolverMap -n bookinfo bookinfo-rest-resolvermap
kubectl delete GraphQLSchema -n bookinfo bookinfo-rest-graphqlschema
kubectl delete RouteTable -n bookinfo graphql-bookinfo