Using GraphQL Persisted Queries to Shrink Requests and Allow-List Operations
Persisted queries replace full GraphQL documents with operation hashes, but automatic registration still sends the full text on first use. Verify your server's behavior before relying on it as an allow-list.
17 Jan 2026, 22:21 UTC

The problem: every request carries the full query
A GraphQL client usually sends the entire operation document in every HTTP request body. For a complex operation with fragments and variables, that document can be several kilobytes. Multiplied across thousands of clients and repeated calls, the payload adds bandwidth cost and makes edge caching harder because the cache key must include or ignore a changing document string.
What a persisted query does
A persisted query stores the GraphQL document on the server ahead of time. The client sends only a short identifier—commonly a SHA-256 hash of the document—instead of the full text. SHA-256 is a cryptographic hash function that produces a fixed-length digest from an input document. The server looks up the hash, retrieves the stored document, and executes it. This is sometimes called an allow-list because the server can reject any request whose hash is not registered.
Important distinction: automatic persisted query (APQ) is not the same as a strict allow-list on day one. In a typical APQ flow, the client sends the hash first. If the server does not recognize it, it returns a documented miss response. The client then retries with the full document, and the server registers the hash for later requests. That first request still transmits the full query, so unregistered operations are not blocked until they have been seen at least once.
Worked example: checking for a miss and registering a query
Run this from a terminal or API client against your GraphQL endpoint. Replace with your server URL and with the SHA-256 hash of a query document you have not yet registered.
# 1. Send only the operation hash
curl -X POST \
-H 'Content-Type: application/json' \
-d '{"queryHash":""}'
# 2. If the server reports a miss, retry with the full document
curl -X POST \
-H 'Content-Type: application/json' \
-d '{"query":"query { user(id: \"1\") { name email } }","queryHash":""}'
Required permissions: none for the client calls themselves; server-side registration may require administrative access depending on the GraphQL server. Expected checks: the first response should match your server's documented miss format—do not assume a specific HTTP status or error code without checking release notes. The second response should execute the operation and, in servers that support APQ, register the hash so later requests can omit query. Risk: if your gateway or server is configured to block unknown hashes, step one will fail before registration is possible.
To verify that repeated calls use the persisted operation, inspect server logs or gateway traces for the operation ID rather than the full document text. Confirm this in a staging environment before changing production client behavior.
Trade-offs and limitations
- First-use exposure: automatic flows transmit the full document on first use, so they do not enforce an allow-list until the query has been registered.
- Hash verification: SHA-256 collisions are unlikely, but the server should still confirm the stored document matches the hash before execution.
- Schema drift: stored queries can become stale after schema changes. Validate or re-register persisted queries in CI/CD alongside deployments.
- Server differences: Apollo Server, GraphQL Yoga, and AWS AppSync have supported persisted queries or similar allow-listing mechanisms, but exact APIs, hash algorithms, and defaults are version-specific.
Closing: decide before you deploy
Persisted queries are useful for reducing request size and stabilizing cache keys, but treat automatic registration as a convenience, not a security boundary. Check your GraphQL server's release notes for the supported hash algorithm and HTTP transport, test the miss-and-retry flow in staging, and add persisted-query validation to your CI/CD pipeline so stored operations stay current with the schema.
This draft requires current verification against your server version before production use.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.