Optimizing Query Performance

1. Understanding Query Complexity

DimensionDetail
DepthNested levels of selection
BreadthNumber of sibling fields
MultipliersList args (first × childCost)
Resolver costPer-field weight

2. Implementing Depth Limiting

Example: graphql-depth-limit

import depthLimit from "graphql-depth-limit";
new ApolloServer({ schema, validationRules: [depthLimit(7)] });

3. Implementing Complexity Analysis

Example: Cost analysis

import { createComplexityRule, simpleEstimator, fieldExtensionsEstimator } from "graphql-query-complexity";

validationRules: [
  createComplexityRule({
    estimators: [fieldExtensionsEstimator(), simpleEstimator({ defaultComplexity: 1 })],
    maximumComplexity: 1000,
    onComplete: (cost) => logger.info({ cost })
  })
]

4. Setting Query Timeouts

LayerMechanism
HTTP serverrequest/socket timeout (e.g. 30s)
DB querystatement_timeout in Postgres
ResolverAbortController + Promise.race

5. Using Persisted Queries

BenefitDetail
Smaller payloadsSend hash instead of query string
CDN cachingGET + hash key cacheable at edge
AllowlistingReject unknown queries (safe-listing)

6. Implementing Query Batching

PatternDetail
Apollo BatchHttpLinkSend array of operations
ServerProcess each, return array of responses
Trade-offLatency drop ↔ harder to cache HTTP

7. Using APQ

Example: Automatic Persisted Queries (Apollo)

// Client first sends only sha256Hash
{ "extensions": { "persistedQuery": { "version": 1, "sha256Hash": "abc..." } } }

// On miss, server returns PERSISTED_QUERY_NOT_FOUND → client resends with full query
// Server caches: hash → query string

8. Implementing Field-level Caching

Example: Cache hint

type Product @cacheControl(maxAge: 300) {
  id: ID!
  price: Money! @cacheControl(maxAge: 60)
  inventory: Int! @cacheControl(maxAge: 30, scope: PRIVATE)
}

9. Optimizing Database Queries

TechniqueDetail
DataLoaderEliminate N+1
Selective columnsInspect info to project only requested fields
JOIN-awareSingle JOIN when child fields are requested
EXPLAIN ANALYZEVerify index usage

10. Using Database Indexes

Index TypeUse
B-treeEquality, range, sort
CompositeCover (filter, sort) columns
PartialWHERE deletedAt IS NULL
GIN/GISTFull-text, JSONB, arrays

11. Implementing Read Replicas

RoutingDetail
Query → replicaDistribute reads
Mutation → primaryMaintain consistency
Read-after-writePin to primary briefly after write
Lag handlingTolerate seconds of staleness or read primary