gRPC resolvers

Define gRPC schema, define gRPC resolvers in your Gloo environment to resolve schema fields, and map schema to resolvers. If you use automatic generation, Gloo defines these configurations for your gRPC API in the ApiDoc, GraphQLResolverMap, and GraphQLSchema Gloo GraphQL CRs. If you want to fine-tune your configuration instead, you can manually create the Gloo GraphQL CRs.

Automatically generate gRPC resources

Automatically generate gRPC API configuration from an existing gRPC server or compiled protobuf definition in the ApiDoc, GraphQLResolverMap, and GraphQLSchema CRs. This generation process is ideal for developers who want to quickly create GraphQL APIs without having to manually write the schema from scratch.

  1. Decide how you want to provide your gRPC protobuf schema to the generation tool. The tool can connect to a gRPC server and issue a reflection request, or you can provide a base64-encoded, compiled protobuf file descriptor set.

    • Server address: Specify the address of the gRPC server to which the tool issues a gRPC reflection request. The tool uses the reflection response to generate Gloo GraphQL resources.
    • Protobuf file descriptor set: Provide the base64-encoded, compiled protobuf file descriptor set. For more information, see the protobuf reference for FileDescriptorSet.
  2. Generate the Gloo GraphQL CRs for your gRPC API. To use fully qualified type names in the generated GraphQL schema, such as to change io.solo.MyType to io_solo_MyType, include the --use-fully-qualified-type-names flag.

    • Server address:
      meshctl generate graphql grpc -n <namespace> --grpc-server-address <http://grpcserveraddress.com>
      
    • Protobuf file descriptor set:
      meshctl generate graphql grpc -n <namespace> --proto-base64 <base64_encoded_file_descriptor_set>
      
  3. Check the GraphQL resources that were generated. To find out more about these resources, you can check out the API reference.

    • The ApiDoc CR contains the gRPC schema for your gRPC API, such as your protobuf descriptors.
      kubectl get ApiDoc -n <namespace>
      
    • The GraphQLResolverMap CR defines gRPC resolver servers for each query type.
      kubectl get GraphQLResolverMap -n <namespace>
      
    • The GraphQLSchema Gloo CR maps the types and fields from your schema definition to the resolver servers.
      kubectl get GraphQLSchema -n <namespace>
      
  4. Now that your gRPC API is represented in your Gloo environment, continue to set up routing to resolvers and apply traffic policies.

Manually create gRPC resources

If you want to fine-tune your gRPC GraphQL resources, you can manually create the ApiDoc, GraphQLResolverMap, and GraphQLSchema Gloo CRs.

Step 1: gRPC schema

Start by defining your schema in an ApiDoc Gloo custom resource (CR).

Define the protobuf descriptors that represent the gRPC services provided by your API, encoded in base64. For more information, see the protobuf reference for FileDescriptorSet.

apiVersion: apimanagement.gloo.solo.io/v2
kind: ApiDoc
metadata:
  name: 
  namespace: 
spec:
  grpc:
    descriptors: <base64_encoded_file_descriptor_set>

In this example for a basic user service app, the base64-encoded descriptor includes a set of fields that are defined for various queries, such as UserSearch and UserByCountry.

apiVersion: apimanagement.gloo.solo.io/v2
kind: ApiDoc
  name: bookinfo-grpc-apidoc
  namespace: bookinfo
spec:
  grpc:
    descriptors: CqIDChd1c2VyX3NlcnZpY2UvdXNlci5wcm90bxIEdXNlciKBAQoEVXNlchIdCgpmaXJzdF9uYW1lGAEgASgJUglmaXJzdE5hbWUSGwoJbGFzdF9uYW1lGAIgASgJUghsYXN0TmFtZRIaCgh1c2VybmFtZRgDIAEoCVIIdXNlcm5hbWUSIQoMY291bnRyeV9jb2RlGAUgASgJUgtjb3VudHJ5Q29kZSIoCgpVc2VyU2VhcmNoEhoKCHVzZXJuYW1lGAEgASgJUgh1c2VybmFtZSIyCg1Vc2VyQnlDb3VudHJ5EiEKDGNvdW50cnlfY29kZRgBIAEoCVILY291bnRyeUNvZGUiKQoFVXNlcnMSIAoFdXNlcnMYASADKAsyCi51c2VyLlVzZXJSBXVzZXJzMmwKC1VzZXJTZXJ2aWNlEicKB0dldFVzZXISEC51c2VyLlVzZXJTZWFyY2gaCi51c2VyLlVzZXISNAoQR2V0VXNlckJ5Q291bnRyeRITLnVzZXIuVXNlckJ5Q291bnRyeRoLLnVzZXIuVXNlcnNiBnByb3RvMw==

Step 2: gRPC resolvers

Define gRPC resolver servers for each query type in a GraphQLResolverMap Gloo CR.

apiVersion: apimanagement.gloo.solo.io/v2
kind: GraphQLResolverMap
metadata:
  name: 
  namespace: 
spec:
  # 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 gRPC API
          resolvers:
            # See section "Optional: Apply jq filters"
          - resolverResultTransform:
              jq: 
            grpcResolver:
              # Reference to the upstream destination associated with the gRPC API
              destinations:
                - port:
                    number: 
                  ref:
                    cluster: 
                    name: 
                    namespace: 
              request:
                # Optional: The ':authority' header to set on the gRPC request
                authority: 
                # The gRPC method to call
                method: 
                # The gRPC request body, which is a JSON object that is marshalled
                # into the gRPC request. Can be templated using variables 
                # from the variables field. For example, see section
                # "Optional: Apply jq filters"
                outgoingMessage:
                  jq: 
                # The gRPC service to call
                service: 
                # Static metadata to be added to the gRPC request. For more info, see
                # https://grpc.io/docs/what-is-grpc/core-concepts/#metadata 
                requestMetadata:
              # The span name for the upstream gRPC request, used for tracing.
              # If unset, the request span name is set to the upstream cluster name
              spanName:
              # The timeout of the HTTP request to the gRPC service (default: 5s)
              timeout: 
          # 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, a gRPC resolver is defined for the GetUser field of the top-level query type. This resolver references the users-grpc service in the cluster.

apiVersion: apimanagement.gloo.solo.io/v2
kind: GraphQLResolverMap
metadata:
  name: bookinfo-grpc-resolvermap
  namespace: bookinfo
spec:
  types:
    Query:
      fields:
        GetUser:
          resolvers:
          - grpcResolver:
              destinations:
              - port:
                  number: 8080
                ref:
                  name: users-grpc
                  namespace: bookinfo
              request:
                method: GetUser
                outgoingMessage:
                  jq: .userInputArg
                service: user.UserService
          variables:
            userInputArg:
              graphqlArg: user

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 3: 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-grpc-resolvermap are mapped to the schema definition from bookinfo-grpc-apidoc.

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

Reference

For more information, see the Gloo Gateway API reference for the following CRs:

Next steps

Set up routing to resolvers and apply traffic policies.