GraphQL персистентні запити
Persisted Queries—техніка, при якій клієнт відправляє хеш запиту замість повного тіла. Сервер кешує запити за хешем. Переваги: зменшений трафік (особливо на mobile), можливість GET-запитів для кешування на CDN, захист від довільних запитів у продакшені.
Як працює Automatic Persisted Queries (APQ)
Клієнт Сервер CDN/Cache
│ │ │
│ POST {hash} │ │
│───────────────>│ │
│ 404 Not Found │ │
│<───────────────│ │
│ │ │
│ POST {hash + query body} │
│───────────────>│ │
│ {data} [зберегти hash→query] │
│<───────────────│ │
│ │ │
│ GET ?hash=... │ │
│──────────────────────────────> │
│ {data} з кешу │
│<────────────────────────────── │
Apollo Client: налаштування APQ
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client'
import { createPersistedQueryLink } from '@apollo/client/link/persisted-queries'
import { generatePersistedQueryIdsFromManifest } from '@apollo/persisted-query-lists'
import { sha256 } from 'crypto-hash'
// Підхід 1: Automatic Persisted Queries (fallback при промахі кешу)
const persistedQueryLink = createPersistedQueryLink({
sha256,
useGETForHashedQueries: true // GET запити для CDN кешування
})
const httpLink = createHttpLink({
uri: 'https://api.example.com/graphql'
})
const client = new ApolloClient({
link: persistedQueryLink.concat(httpLink),
cache: new InMemoryCache()
})
Серверна підтримка APQ
// Вбудована підтримка APQ в Apollo Server
import { ApolloServer } from '@apollo/server'
import { createClient } from 'redis'
import { BaseRedisCache } from 'apollo-server-cache-redis'
const redis = createClient({ url: process.env.REDIS_URL })
await redis.connect()
const server = new ApolloServer({
typeDefs,
resolvers,
// APQ cache: in-memory за замовчуванням, Redis у продакшені
cache: new BaseRedisCache({
client: redis,
ttl: 86400 // 24 години
}),
// Включити APQ (за замовчуванням включено)
persistedQueries: {
ttl: 86400 // Час життя в кеші
}
})
Registered Persisted Queries (безпечніший підхід)
APQ дозволяє виконувати будь-який запит після реєстрації. Registered PQ дозволяє тільки заранее відомі запити:
# Генерування маніфесту з клієнтських операцій (при збиранні)
npx generate-persisted-query-manifest \
--documents "src/**/*.graphql" \
--output persisted-query-manifest.json
// persisted-query-manifest.json
{
"format": "apollo-persisted-query-manifest",
"version": 1,
"operations": [
{
"id": "dc67510fb4289672bea757e862d6b00e...",
"name": "GetPosts",
"type": "query",
"body": "query GetPosts($limit: Int) { posts(first: $limit) { ... } }"
},
{
"id": "e8c72e4d9b1a2f8e5c3d7a9b1f4e8c9d...",
"name": "CreatePost",
"type": "mutation",
"body": "mutation CreatePost($input: CreatePostInput!) { createPost(input: $input) { ... } }"
}
]
}
// Сервер валідує проти маніфесту
import { ApolloServer } from '@apollo/server'
const allowedQueries = new Set(
manifest.operations.map(op => op.id)
)
const server = new ApolloServer({
typeDefs,
resolvers,
persistedQueries: {
ttl: 86400
},
// Дозволити тільки зареєстровані запити
plugins: [{
async requestDidResolveOperation({ request }) {
const id = request.http?.headers?.get('x-graphql-query-id')
if (id && !allowedQueries.has(id)) {
throw new Error('Persisted query not found')
}
}
}]
})
Переваги
- Зменшений трафік: на 50-70% менші запити на mobile
- Кешування CDN: GET запити можуть кешуватися стандартним CDN
- Whitelisting запитів: запобігає небажаним запитам у продакшені
- Захист від DDoS: тільки відомі запити виконуються на сервері
Терміни
Реалізація APQ (автоматичний підхід): 1–2 дні. Registered PQ з генерацією маніфесту та інтеграцією CI/CD: 2–3 дні.







