Performance & Caching
GraphQL performance challenges: semua request POST (no HTTP caching), arbitrary query depth, N+1 queries.
Query Complexity & Depth Limiting
// Prevent malicious deep queries
// query { user { posts { comments { author { posts { comments { ... } } } } } } }
import depthLimit from "graphql-depth-limit";
import { createComplexityLimitRule } from "graphql-validation-complexity";
const server = new ApolloServer({
typeDefs,
resolvers,
validationRules: [
depthLimit(5), // max 5 levels deep
createComplexityLimitRule(1000), // max complexity score
],
});
Persisted Queries
// Client kirim hash, bukan full query string
// Mengurangi request size dan bisa di-whitelist
// Request (tanpa persisted):
POST /graphql
{ "query": "query GetUser($id: ID!) { user(id: $id) { name } }", "variables": { "id": "1" } }
// Request (dengan persisted):
POST /graphql
{ "extensions": { "persistedQuery": { "sha256Hash": "abc123..." } }, "variables": { "id": "1" } }
Response Caching
// Cache control directive
type User @cacheControl(maxAge: 300) {
id: ID!
name: String! @cacheControl(maxAge: 3600) # name rarely changes
email: String! @cacheControl(maxAge: 0) # never cache email
}
// CDN caching via GET requests (Apollo)
// ?query=...&variables=... → cacheable di CDN
Client-side Caching (Apollo)
// Apollo Client normalizes cache by __typename + id
const client = new ApolloClient({
cache: new InMemoryCache(),
// After mutation, cache auto-updates if same id returned
});
// Manual cache update
cache.modify({
id: cache.identify(user),
fields: {
name: () => "New Name",
},
});
Batching
// Multiple queries in one HTTP request
[
{ "query": "query { user(id: 1) { name } }" },
{ "query": "query { posts { title } }" }
]
// Server proses parallel, return array of responses