REST resolvers

Define REST resolver servers for each query type in a GraphQLResolverMap Gloo custom resource. Then, map fields from your schema definition to resolvers in a GraphQLSchema CR.

Step 1: REST resolvers

Define your REST resolvers in a GraphQLResolverMap CR.

apiVersion: apimanagement.gloo.solo.io/v2
kind: GraphQLResolverMap
metadata:
  name: 
  namespace: 
spec:
  extensions:
    # Selects any API schema objects that describe a gRPC service your REST 
    # resolver must use. The gRPC resolver uses the protobuf descriptor to
    # create gRPC requests to the upstream gRPC service.
    grpcSchemaSelector:
      cluster: 
      name: 
      namespace: 
  # Resolver map
  types:
    Query:
      fields:
        # Replace 'nameOfField' with the name of the field that you 
        # previously defined for this query
        <nameOfField>:
          # Configuration for generating outgoing requests to a REST API
          resolvers:
            # See section "Optional: Apply jq filters"
          - resolverResultTransform:
              jq: 
            restResolver:
              # Reference to the upstream destination associated with the REST API
              destinations:
                - port:
                    number: 
                  ref:
                    cluster: 
                    name: 
                    namespace: 
              # Template an HTTP request that fetches JSON responses from a destination REST service
              request:
                # Set headers dynamically on the HTTP request
                headers:
                  # HTTP method (POST, PUT, GET, DELETE, etc.) 
                  :method:
                    value: 
                  # Path portion of upstream API URL. Can reference variables, jq transformations, and values
                  :path:
                    jq:
                  # User-defined headers (key/value)
                  headerName:
                    key: value
                # URL query parameters (key/value) on the request to the REST destination
                queryParams:
                  key: value
                # Request body content (primarily for PUT, POST, PATCH)
                body:
          # Declare any variables for transformations applied to the GraphQL request or response
          # in the 'resolverResultTransform', 'headers', and 'queryParams' fields.
          # For more info, see section "Optional: Define variables for use in resolvers"
          variables:

For further information about how resolvers can apply transformations on the requests to or responses from services:

In this example resolver map, resolvers are defined for the fields that you specified in the example bookinfo-rest-apidoc ApiDoc.

Query types: For the top-level query type, a resolver is defined for the productsForHome field. This REST resolver is the productpage Bookinfo service that you deployed to your cluster. A header with the API path for the service, /api/v1/products, is added to the query request.

Object types: For the Product object type, resolvers are defined for the ratings and reviews fields. These REST resolvers are the ratings and reviews Bookinfo services, respectively, that you deployed to your cluster.

Transformations:

  • On requests to each of these services, a header with the API path for the service and the value for the id field is added. This ID field is retrieved from the .parentVar variable, which is defined in the variables section of each resolver. This variable retrieves the id field from the Product parent object. Then, a jq filter is applied to convert the ID data to a string, and append it to the ratings or review path. For more information, see Define variables.
  • On the response from the ratings service, a jq filter is applied to the result data. For example, the result data from ratings might not be formatted in a way that a 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. The data in the response is returned such as {"reviewer":"Reviewer1","numStars":5},{"reviewer":"Reviewer2","numStars":4}. For more information, see Apply jq filters.
  • On the response from the reviews service, a jq filter is also applied. 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.
apiVersion: apimanagement.gloo.solo.io/v2
kind: GraphQLResolverMap
metadata:
  name: bookinfo-rest-resolvermap
  namespace: bookinfo
spec:
  types:
    Query:
      fields:
        productsForHome:
          resolvers:
          - restResolver:
              destinations:
              - port:
                  number: 9080
                ref:
                  name: productpage
                  namespace: bookinfo
              request:
                headers:
                  :path:
                    jq: '"/api/v1/products"'
    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
              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
              request:
                headers:
                  :path:
                    jq: '"/reviews/" + (.parentVar.id | tostring)'
          variables:
            parentVar:
              graphqlParent: {}
            resolverResultVar:
              resolverResult: {}

Optional: Define variables for use in resolvers

You can define variables that you want to use in the variables section of a resolver in the resolver map. For example, you might define variables for the following uses:

Note that some variables have defined names and guidelines for values. For more information about each type of variable, see the API documentation for resolver variables.

For example, you might define the following variables for the Reviews service:

...
Product:
  fields:
    reviews:
      variables:
        parentVar:
          graphqlParent: {}
        authorizationVar:
          requestHeader: "Authorization"

In the same resolver map resource, you can then call these variables in the resolver request headers or body, such as the following headers. Note that you can also specify individual values that are not declared in variables.

...
resolvers:
- restResolver:
    destinations:
      ...
    request:
      headers:
        Authorization:
          variable: authorizationVar
        :path:
          variable: parentVar
          value: "[1,2,3]"

Optional: Apply jq filters

To transform the JSON content of a GraphQL resolver request or result, you can provide jq filters in your GraphQL resolver map.

You can define variables that you want to use in the jq transformation filter in the variables section of a resolver in the resolver map. For example, a variable named “userIdHeader” can be used in a jq filter as .userIdHeader. Then, you can apply jq filters to resolver requests (resolver.restResolver.request) or results (resolver.restResolver.resolverResultTransform) that call these variables.

For more information, see the API documentation for jq transformation. For information about jq syntax and how to construct your jq filter, see the jq development manual.

For example, you might define the following variables for the Reviews service:

...
Product:
  fields:
    reviews:
      variables:
        parentVar:
          graphqlParent: {}
        authorizationVar:
          requestHeader: "Authorization"

You can then call these variables in the resolver request headers or body:

...
resolvers:
- restResolver:
    destinations:
      ...
    request:
      headers:
        :path:
          jq: '"/reviews/" + .parentVar.id'
        customAuth:
          jq: '"Bearer " + .authorizationVar'

You can also use variables in jq filters for resolver results. For example, if the data that the resolver fetched from your upstream API is not formatted in a way that the client can understand according to your schemas, you can apply a jq filter to the result data.

The fetched data from your API might be formatted like the following:

{  "userIdHeader": "john_doe123",  "resolverResultVar": { "data": {"name": "John Doe"} } }

First, you define the following variables.

...
variables:
  userIdHeader:
    requestHeader: x-user-id
  resolverResultVar:
    resolverResult: {}

You then call those variables in a resolver.resolverResultTransform jq filter. Note that the resolverResultVar variable can only be used in the resolverResultTransform field, and not in a request field.

...
resolvers:
- restResolver:
    destinations:
      ...
    request:
      ...
  resolverResultTransform:
    jq: '"User: " + .userIdHeader + ", Name: " + .resolverResultVar.data.name'

The result of the jq transformation is as follows:

"User: john_doe123, Name: John Doe"

jq filters must only result in one value. For example, for the input [1,2,3], the jq filter .[] is not a valid jq filter as it results in multiple jq results. However, the jq filter '. | join(",")' is valid as it results in only one result: “1,2,3”. Refer to the jq manual for jq syntax and tips.

Step 2: Map schema to resolvers

Map the types and fields from your schema definition (ApiDoc) to the resolver servers (GraphQLResolverMap) in a GraphQLSchema Gloo CR. The GraphQLSchema CR ensures that the GraphQL resolver services can access the field information for each type.

apiVersion: apimanagement.gloo.solo.io/v2
kind: GraphQLSchema
metadata:
  name: 
  namespace: 
spec:
  # Allow GraphQL servers that you define in GraphQLResolverMap CRs to resolve queries locally
  resolved:
    # Additional options on the schema
    options:
      # Enable introspection for the schema. Defaults to false.
      enableIntrospection:
      # Limit the maximum nesting on a query that runs against this schema.
      # Any GraphQL operation that queries past the max depth adds an error message to the response and returns as null.
      # If not configured, or the value is 0, the query depth is unbounded.
      maxDepth:
    # References to GraphQLResolverMap resources for resolvers.
    # Resolver maps that are higher in this list have a higher priority
    # over lower items when tie-breaking field resolver configurations exist.
    resolverMapRefs:
      - clusterName: 
        name: 
        namespace: 
  # Reference to ApiDoc resource for schema definitions
  schemaRef:
    clusterName: 
    name: 
    namespace: 

In this example GraphQLSchema resource, the resolvers from bookinfo-rest-resolvermap are mapped to the schema definition from bookinfo-rest-apidoc.

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

Reference

For more information, see the Gloo Gateway API reference for the GraphQLResolverMap CR and GraphQLSchema CR.

Next steps

Set up routing to resolvers and apply traffic policies.