GraphQL vs REST for Data Access Cheat Sheet
Compares GraphQL and REST for data-fetching patterns, covering over/under-fetching, endpoint design, caching, and N+1 query pitfalls.
2 PagesIntermediateMar 5, 2026
Request Comparison
Fetching related data with REST versus GraphQL.
javascript
// REST: multiple round trips to avoid over-fetching, or one bloated endpoint// GET /api/users/42// GET /api/users/42/posts// GET /api/posts/17/comments// GraphQL: one request, client specifies exactly the fields it needsconst query = ` query { user(id: 42) { name posts { title comments { text } } } }`;const res = await fetch('/graphql', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query }),});
Trade-offs
How the two approaches differ in practice.
- Over-fetching- REST endpoints often return fixed shapes with more fields than the client needs; GraphQL clients request exact fields
- Under-fetching- REST may require multiple round trips to assemble related data; GraphQL resolves nested relations in a single request
- Caching- REST benefits from standard HTTP caching (ETag, Cache-Control, CDNs) keyed by URL; GraphQL POST requests need custom client-side caching (Apollo, Relay normalized cache)
- Versioning- REST typically versions endpoints (/v1, /v2); GraphQL favors additive schema evolution with field deprecation instead of versioning
- Error handling- REST uses HTTP status codes per request; GraphQL usually returns 200 with an errors array, requiring clients to check payload-level errors
- Tooling- GraphQL ships a strongly typed schema enabling introspection, codegen, and interactive explorers (GraphiQL); REST relies on external specs like OpenAPI for the same
Avoiding N+1 with DataLoader
Batch per-request lookups into a single query.
javascript
const DataLoader = require('dataloader');// Batches individual post.author lookups into one SQL query per tickconst userLoader = new DataLoader(async (userIds) => { const users = await db.query( 'SELECT * FROM users WHERE id = ANY($1)', [userIds] ); const byId = Object.fromEntries(users.map((u) => [u.id, u])); return userIds.map((id) => byId[id]);});const resolvers = { Post: { author: (post) => userLoader.load(post.authorId), },};
When to Use Which
Guidance for picking an API style for data access.
- Choose REST when- You need simple CRUD, strong HTTP caching, public/partner APIs, or your clients' data needs are uniform and stable
- Choose GraphQL when- Clients have diverse, evolving data needs (e.g., web + mobile with different field requirements), or you're aggregating multiple backend services
- Hybrid approach- Many teams expose REST for simple public endpoints and GraphQL (or a BFF) for complex, client-driven aggregation
- Team/ops cost- GraphQL requires more upfront investment: schema design, resolver N+1 mitigation, query complexity/depth limiting to prevent abuse
Pro Tip
GraphQL's flexibility is also its biggest operational risk — without query depth limiting, complexity analysis, and persisted queries, a single malicious or buggy client query can fan out into thousands of database calls; treat query cost limiting as a launch requirement, not an afterthought.
Was this cheat sheet helpful?
Explore Topics
#GraphQLVsRESTForDataAccess#GraphQLVsRESTForDataAccessCheatSheet#Database#Intermediate#RequestComparison#TradeOffs#AvoidingN1WithDataLoader#WhenToUseWhich#Databases#APIs#CheatSheet#SkillVeris