Skip to main content

GraphQL: Query Caching

In GraphQL, the query and the parsed query refer to different stages of handling a GraphQL request.

query GetUser {
user(id: "123") {
id
name
email
}
}

This is the text or string version of the GraphQL request that a client sends to the server. It is human-readable and typically written in the GraphQL query language.

When server receive the query request, the server then parse it to validate the query with the schema available and add some query enhancement if possible.

Parsed query is a structured data format (usually a tree: AST) that represents the query's components (fields, arguments, fragments, etc.).

Example of parsed query representation of above query:

{
"Operation": "query",
"Name": "GetUser",
"SelectionSet": [
{
"Field": "user",
"Arguments": {
"id": "123"
},
"SelectionSet": [
{ "Field": "id" },
{ "Field": "name" },
{ "Field": "email" }
]
}
]
}

Parsing is CPU-intensive, so caching the parsed query (AST) speeds up repeated requests by skipping the parsing step.

Parsed Query Cache

The implementation is simple we just need to store the parsed query with key using hashed full query.

In golang gqlgen libraries it has function SetQueryCache which stores the parsed query (AST), so if the same query is sent again, the server can skip the parsing step.

srv.SetQueryCache(lru.New[*ast.QueryDocument](1000))

lru in here is just simple in-memory cache. Because this function accept interface as parameter we can create our own custom cache using redis, etcd or any other caching provider we want for distributed systems.

APQ: Automatic Persisted Queries

APQ optimize the operations clients send to a GraphQL server. Instead of sending the full operation string, the client sends an operation identifier: the unique hash of the operation string.

If hash is not found on a server then client makes a second request to register query hash with original query on a server. In order to enable Automatic Persisted Queries we need to change our client.

How it Works

  • On the first query request:
    • Client sends the full query and hashed query.
    • Server validate the hash.
    • Server caches the query with a unique hash.
    • Execute the Query
  • On subsequent requests:
    • Client sends only the query hash.
    • Server looks up the query from the APQ cache.
    • Execute the Query

Advantages

  • Reduces payload size for repeated queries.
  • Ideal for mobile clients or low-bandwidth environments.

You can find implementation example using golang in this guide.

Difference between APQ and PQ (Persisted Queries)

Persisted Queries (PQ) are exclusive list of queries that predefined and stored on the server, and clients only send a query ID to execute them. This list of queries are trusted operations and server can reject any queries request that are not in the list.

This provide better security because client can only execute predefined queries. Usually used for mutable operations.

References