Schema definition language, queries/mutations/subscriptions, resolvers, the N+1 problem and DataLoader, auth in GraphQL — and when to choose GraphQL over REST.
Module P-11 — GraphQL with Apollo Server
What this module covers: REST and GraphQL solve the same problem — exposing data over HTTP — with different trade-offs. GraphQL gives clients precise control over what data they receive, eliminates over-fetching and under-fetching, and makes schema changes visible and versioned. This module covers the Schema Definition Language, building a GraphQL API with Apollo Server and Express, writing resolvers for queries and mutations, implementing subscriptions for real-time data, solving the N+1 problem with DataLoader, adding authentication, and knowing when to reach for GraphQL instead of REST.
REST vs GraphQL: The Core Trade-Off
Consider a mobile screen showing a user's profile with their five most recent posts and follower count.
REST approach — three requests:
Problems:
- Three round-trips (three network waterfalls on mobile)
/users/42returns 20 fields; the screen needs 4- You're either over-fetching (wasting bandwidth) or under-fetching (needing more calls)
- Adding a new screen requirement changes which endpoints you need to call
GraphQL approach — one request, exactly the data needed:
One request, four fields from user, three fields per post. The client defines the shape. Adding a new screen just changes the query.
GraphQL shines when: you have multiple clients with different data needs (web, mobile, partner integrations), your data is deeply relational, or your API is consumed by teams you don't control.
REST shines when: you have a simple CRUD API, need HTTP caching, are building a public API that tools should be able to discover automatically, or your team knows REST well.
Setup: Apollo Server with Express
Context: Authentication in GraphQL
GraphQL doesn't have middleware like Express. Authentication goes into the context function — called once per request and passed to every resolver:
Resolvers
Resolvers are functions that return the data for each field. They receive (parent, args, context, info):
Type resolvers — resolve fields that need additional data fetching:
The N+1 Problem and DataLoader
The most common GraphQL performance bug. A query for 20 posts with their authors:
The Post.author resolver runs 20 times, each doing SELECT * FROM users WHERE id = ?. That's 21 queries total (1 for posts + 20 for authors).
DataLoader batches and caches these lookups:
Add loaders to context:
Use loaders in resolvers:
Now the same query for 20 posts produces just 2 queries: one for posts, one SELECT WHERE id IN (1, 5, 7, ...) for all authors. DataLoader collected all the load() calls in a tick, batched them, and resolved them together.
Subscriptions with WebSocket
GraphQL subscriptions deliver real-time updates over WebSocket:
For production, replace graphql-subscriptions PubSub (in-memory, single server) with graphql-redis-subscriptions (Redis-backed, multi-server).
Wiring Apollo Server to Express
Open http://localhost:3000/graphql — Apollo Studio Sandbox provides an in-browser IDE for exploring the schema and sending queries.
GraphQL Error Handling
GraphQL always returns 200 OK — errors are in the errors array of the response body. The extensions.code is the convention for programmatic error handling by clients.
When to Choose GraphQL vs REST
Choose GraphQL when:
- Multiple clients (web, mobile, partner) need different data shapes from the same API
- Data is highly relational (users → posts → comments → likes → users…)
- You want self-documenting APIs — the schema IS the documentation
- Your frontend team wants to move fast without waiting for backend changes
- Rapid iteration on new screens/features
Choose REST when:
- Simple CRUD operations with predictable resource shapes
- You need HTTP-level caching (CDN caching of GET responses)
- Building a public API that must be discoverable by generic tools
- File uploads are a primary concern (GraphQL handles these awkwardly)
- Your team knows REST deeply and the API is not complex
Mixing both is valid. Many large companies run REST for public APIs (stable, cacheable, simple) and GraphQL for internal front-end APIs (flexible, fast to iterate). Your Node.js app can serve both on the same server.
Summary
- GraphQL solves over-fetching and under-fetching — clients request exactly the fields they need in a single round-trip. REST solves discoverability, caching, and simplicity.
- Schema Definition Language is the contract. Define types, queries, mutations, and subscriptions before writing a single resolver.
- Context function runs once per request — the right place to verify tokens and attach user data for all resolvers.
- DataLoader is non-negotiable. Any
Post.authorresolver that queries the DB directly will produce N+1 queries. Batch all related-entity lookups. - Subscriptions use WebSocket under the hood.
graphql-wsis the current standard. Replace in-memory PubSub with Redis-backed PubSub for multi-server deployments. - GraphQL errors return 200 with an
errorsarray. Useextensions.codefor programmatic handling. Never expose internal error details in production.
Next: background jobs with BullMQ — deferring slow work off the request path, retries with exponential backoff, scheduled jobs, job priorities, and concurrency control.
In the context of GraphQL, what problem does DataLoader primarily solve?
In an Apollo Server Express setup, where is the most appropriate place to perform authentication (e.g., verifying a JWT) so that the user's information is available to all resolvers?
How does error handling in a standard GraphQL API typically differ from a REST API?
Test your knowledge with more question sets
Sign in to access a wider variety of questions and get notified when new practice sets are added to this module.
Sign in & RegisterDiscussion
0Join the discussion