Bandwidth savings, security hardening, and pre-compiled query execution.
Automatic Persisted Queries (APQ) allows clients to send a short hash instead of the full GraphQL query text. The server stores registered queries and looks them up by hash, providing bandwidth savings and enabling security hardening.
APQ persists the query definition (the GraphQL text), not query results. Each request still executes against the database with fresh data. The benefits are bandwidth reduction and skipping query parsingβnot result caching.
| Benefit | Without APQ | With APQ |
|---|---|---|
| Request Payload | Full query text (1-10KB) | SHA256 hash (64 bytes) |
| Bandwidth Usage | 100% | ~30% (70% reduction) |
| Query Parsing | Parse on every request | Pre-parsed, lookup only |
| Security Control | Any query accepted | Can enforce registered-only |
Client FraiseQL Database
β β β
β POST /graphql β β
β { "extensions": { β β
β "persistedQuery": { β β
β "sha256Hash": "abc123" β β
β } β β
β } β β
β } β β
βββββββββββββββββββββββββββββββββββΆβ β
β β β
β 1. Lookup hash in β
β APQ store β
β β β
β 2. Get pre-parsed β
β query definition β
β β β
β 3. Execute query ββββββββββββββββββββββββΆβ
β β β
β ββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββ β
β { "data": { ... } } β β
# Client sends full query + hash
POST /graphql
{
"query": "query GetUsers { users { id name } }",
"extensions": {
"persistedQuery": {
"version": 1,
"sha256Hash": "abc123..."
}
}
}
# FraiseQL:
# 1. Validates hash matches query
# 2. Stores query in APQ store
# 3. Executes and returns result
# Client sends only the hash
POST /graphql
{
"extensions": {
"persistedQuery": {
"version": 1,
"sha256Hash": "abc123..."
}
}
}
# FraiseQL:
# 1. Looks up query by hash
# 2. Uses pre-parsed query
# 3. Executes and returns result
#
# Bandwidth: ~70% smaller request
Send a 64-byte hash instead of multi-KB query strings. Significant savings for mobile clients and large queries.
Optional "persisted-only" mode blocks arbitrary queries. Only pre-registered queries can execute.
Registered queries are pre-parsed. No need to parse and validate GraphQL on every request.
Queries stored in your database. No Redis needed. Survives restarts, shared across instances.
Track which queries are used, how often, and by which tenants. Built-in observability.
For security-sensitive deployments, FraiseQL can enforce that only pre-registered queries are allowed. Arbitrary queries are rejected.
from fraiseql.fastapi import FraiseQLConfig, create_fraiseql_app
from fraiseql.fastapi.config import APQMode
# Security-hardened: only persisted queries allowed
config = FraiseQLConfig(
database_url="postgresql://localhost/db",
apq_mode=APQMode.REQUIRED, # Block arbitrary queries
apq_queries_dir="./graphql/", # Auto-register .graphql files
)
app = create_fraiseql_app(types=[User, Post], config=config)
# APQ Modes:
# - APQMode.OPTIONAL (default): Accept both hashes and full queries
# - APQMode.REQUIRED: Only accept persisted query hashes
# - APQMode.DISABLED: Ignore APQ extensions, always require full query
from fraiseql.fastapi import FraiseQLConfig, create_fraiseql_app
# APQ enabled by default (optional mode)
config = FraiseQLConfig(
database_url="postgresql://localhost/db",
# apq_mode defaults to APQMode.OPTIONAL
# apq_storage_backend defaults to "memory"
)
app = create_fraiseql_app(types=[User, Post], config=config)
from fraiseql.fastapi import FraiseQLConfig
# Memory Backend (default) - fast, single-server
# Good for development, queries lost on restart
config = FraiseQLConfig(
database_url="...",
apq_storage_backend="memory",
)
# PostgreSQL Backend (recommended) - persistent, production-ready
# Queries survive restarts, shared across instances
config = FraiseQLConfig(
database_url="...",
apq_storage_backend="postgresql",
)
No. APQ caches the query definition (the GraphQL text), not results. Each request still executes against the database with fresh data. For result caching, you'd use HTTP caching or a CDN.
APQ saves bandwidth and skips query parsing, but execution time depends on your database queries. The main win is 70% bandwidth reduction and avoiding repeated parsing of complex queries.
Yes. FraiseQL implements the Apollo APQ specification. Apollo Client, Relay, and other GraphQL clients with APQ support work out of the box.
APQ is enabled by default in FraiseQL. Zero configuration needed.