On this page

Batching for N+1 problem

GraphQL Mesh v0 documentation (superseded by v1): Learn how GraphQL Mesh can solve the N+1 Query problem by batching requests with specific parameters. Check out the multiple-sources example.

A Mesh Gateway that is not properly configured will face the same famous issue as any other GraphQL servers: the N+1 Query problem.

Fortunately, GraphQL Mesh provides a way to batch requests with specific parameters.

Enable requests batching

Our multiple-sources example Gateway:

Mobile app

Web app

Node.js client

Books REST API

Authors gRPC API

Stores GraphQL API

Mesh Gateway GraphQL API

Will emit multiple requests to the “Authors” API when resolving the nested Book.author field:

query bestSellersByStore {
  stores {
    id
    name
    bookSells {
      sellsCount
      book {
        id
        title
        author {
          id
          name
        }
      }
    }
  }
}
Authors APIBook.author resolverMesh GatewayAuthors APIBook.author resolverMesh GatewayQuery.stores[0].bookSells[0].book[0].authorGetAuthor(input: \{ id: "123" \})Query.stores[0].bookSells[0].book[6].authorGetAuthor(input: \{ id: "754" \})Query.stores[0].bookSells[0].book[7].authorGetAuthor(input: \{ id: "332" \})

Fortunately, Mesh allows an extra directive-based configuration to provide a “batching” query that will help resolve many record of the same type.

Our current resolver configuration for Book.author is the following:

.meshrc.yaml

.meshrc.yaml
sources:
  # …
transforms:
  # …
additionalTypeDefs: |
  # …
  extend type Book {
    author: authors_v1_Author @resolveTo(
      sourceName: "Authors", # Which source does the target field belong to?
      sourceTypeName: "Query", # Which root type does the target field belong to?
      sourceFieldName: "authors_v1_AuthorsService_GetAuthor", # What is the source field name?
      requiredSelectionSet: "{ authorId }",
      # What args does this need to take?
      sourceArgs: {
        "input.id": "{root.authorId}"
      }
    )
  }

Assuming that the “Authors” is exposing a authors_v1_AuthorsService_GetAuthors(input: authors_v1_GetAuthorsRequest_Input) with authors_v1_GetAuthorsRequest_Input being:

input authors_v1_GetAuthorsRequest_Input {
  ids: [String!]!
}

We could update our .meshrc.yaml configuration as follows:

.meshrc.yaml
sources:
  # …
transforms:
  # …

# Create a resolver with batching to solve N+1 problem
additionalTypeDefs: |
  # …
  extend type Book {
    author: authors_v1_Author @resolveTo(
      sourceName: "Authors",
      sourceFieldName: "authors_v1_AuthorsService_GetAuthors",
      keyField: "authorId",
      keysArg: "input.ids"
    )
  }

requiredSelectionSet and sourceArgs got replaced by keyField and keysArg:

  • keysArg provides the name of the batching primary key argument (input.ids from authors_v1_GetAuthorsRequest_Input)
  • keyField indicates which Book selection-set field should be used to provide the ids value

By default, batched results are mapped back to keys by array position: the upstream must return one result per key in key order. Collection-style sources that return rows in their own order (or with gaps / multiples) should also set valueKeyField to the field on each result used for correlation — see Schema Extensions → valueKeyField.

Now, our Mesh Gateway will try to batch calls to the “Authors” API when resolving Book.author:

Authors APIBook.author resolverMesh GatewayAuthors APIBook.author resolverMesh GatewayQuery.stores[0].bookSells[0].book[0].authorQuery.stores[0].bookSells[0].book[6].authorQuery.stores[0].bookSells[0].book[7].authorGetAuthors(input: \{ ids: ["123", "754", "332"] \})

Request Batching on the gateway level

Mesh also provides a way to batch requests on the gateway level. This is useful when you want to send multiple requests to the gateway in a single HTTP request. This follows Batching RFC

serve:
  batchingLimit: 10 # You have to define an explicit limit for batching