Get started

Set up API gateway and GraphQL server functionality for your apps in the same process by using Gloo Gateway.

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.

Figure: Gloo custom resources for a GraphQL API

Install GraphQL

  1. Set the Gloo Gateway license with the Gloo GraphQL add-on as an environment variable. To check that your Gloo Gateway license includes the Gloo GraphQL add-on, or to purchase a license, contact an account representative.

    export GLOO_GATEWAY_LICENSE_KEY=<key>
    
  2. Install GraphQL by installing Gloo Gateway for the first time, or by upgrading your existing Gloo Gateway installation.

    GraphQL is supported for Gloo Gateway version 2.1.0 or later, and for gateway proxies that run Istio version 1.16.1 or later.

  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 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. 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
    
  2. 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
    
  3. 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 IP address of your ingress gateway to test the /graphql route.

    export INGRESS_GW_IP=$(kubectl get svc -n gloo-mesh-gateways istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    echo $INGRESS_GW_IP
    
    export INGRESS_GW_IP=$(kubectl get svc -n gloo-mesh-gateways istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
    echo $INGRESS_GW_IP
    

  3. Send a request to the GraphQL endpoint to verify that the request is successfully resolved by the gateway.

    curl -vik --resolve www.example.com:80:${INGRESS_GW_IP} http://www.example.com:80/graphql \
    -d '{"query":"query { productsForHome { title ratings {reviewer numStars}} }"}'
    
    curl -vik --resolve www.example.com:443:${INGRESS_GW_IP} https://www.example.com:443/graphql \
    -d '{"query":"query { productsForHome { title ratings {reviewer numStars}} }"}'
    
    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 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