GraphQL APIs
GraphQL is a query language for APIs where the client asks for exactly the fields it needs from a single, strongly typed endpoint, and the server answers using one resolver function per field.
Learn GraphQL APIs in our free Node.js course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.
Part of the free Node.js course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
In this lesson you'll compare GraphQL with REST, write a schema with types, queries, and mutations, understand how resolvers fetch each field, see the core query executor in action, and wire it all together with Apollo Server.
What You'll Learn in This Lesson
1️⃣ GraphQL vs REST: the Schema
A GraphQL API starts with a schema written in the Schema Definition Language (SDL). It declares your types (like User ), the readable fields under type Query , and the write operations under type Mutation . A ! marks a field as non-null (required).
Where REST might force you to call /users/1 and get the whole record, a GraphQL query names just the fields it wants — so the client decides the response shape, avoiding both over-fetching and under-fetching.
2️⃣ Resolvers: How Fields Get Their Values
The schema says what exists; resolvers say how to fetch it. A resolver is one function per field that returns its value — from a database, another API, or an array. GraphQL then keeps only the fields the client asked for. The runnable executor below models exactly that: it resolves the data, then picks just the requested fields .
3️⃣ The Real Thing: Apollo Server
In production you hand your typeDefs (the SDL) and your resolvers to Apollo Server , which parses each incoming query, validates it against the schema, calls the right resolvers, and assembles the response — plus a sandbox UI for exploring your API. Notice resolvers are grouped under Query and Mutation , and each receives (parent, args, context) .
Your turn. The executor below works once you fill in the field list marked ___ . Follow the 👉 hint, then run it.
Mini-Challenge: A Query + a Mutation
No blanks — just a brief and an outline. Build a resolver map with a Query.todos reader and a Mutation.addTodo writer, then show the list growing. Run it and compare with the example output.
Common Errors (and the fix)
- ❌ "Cannot query field X on type Y" — you asked for a field that isn't in the schema. ✅ Add it to the type's SDL and give it a resolver.
- ❌ A field returns null unexpectedly — its resolver returned nothing, or the parent object used a different key. ✅ Check the resolver's return value and the parent's property names.
- ❌ Mutations that should write don't persist — you defined the field under Query instead of Mutation , or forgot to actually save. ✅ Put writes under Mutation and perform the database/array change inside the resolver.
- ❌ One huge nested query brings the server to its knees — clients can request deeply nested data. ✅ Add query depth/complexity limits and use DataLoader to batch database calls.
- ❌ Non-null ( ! ) field resolved to null throws — GraphQL errors when a required field is missing. ✅ Ensure those resolvers always return a value, or drop the ! .
Pro Tips
- 💡 Design the schema first: agree on types and fields before writing resolvers — the schema is the contract between front end and back end.
- 💡 Use the context for auth and the DB: put the logged-in user and database handle in Apollo's context so every resolver can reach them.
- 💡 Batch with DataLoader: it collapses many per-item database calls into one, fixing the classic N+1 query problem.
📋 Quick Reference — GraphQL Building Blocks
Concept
Detail
Define a type
type User { id: ID! }
Required field
name: String!
Read data
type Query
Write data
type Mutation
Resolver signature
(parent, args, context)
Run a server
new ApolloServer(...)
Fix N+1
DataLoader
Frequently Asked Questions
🎉 Lesson Complete!
- ✅ GraphQL lets the client request exactly the fields it needs from one endpoint
- ✅ The schema (SDL) declares types, queries, mutations, and non-null ( ! ) fields
- ✅ Resolvers are one function per field that fetch its value
- ✅ Queries read; mutations write
- ✅ Apollo Server wires schema + resolvers into a running API
- ✅ Watch out for deep queries and the N+1 problem — limit depth and use DataLoader
- ✅ Next lesson: SQL Databases with Sequelize — model and query relational data
Practice quiz
What is a key advantage of GraphQL over a typical REST endpoint?
- It requires no server
- The client asks for exactly the fields it wants
- It only returns XML
- It cannot be queried
Answer: The client asks for exactly the fields it wants. A GraphQL client requests precisely the fields it needs, no more, no less.
In GraphQL SDL, what does the ! after a type mean, as in String!?
- The field is non-null (required)
- The field is deprecated
- The field is a list
- The field is optional
Answer: The field is non-null (required). ! marks a field as non-null, meaning it must always have a value.
What is a resolver in GraphQL?
- A database table
- A caching layer
- A function that returns the value for a field
- A type definition
Answer: A function that returns the value for a field. Each field has a resolver function that produces its value.
Which special type holds read operations in a GraphQL schema?
- Mutation
- Subscription
- Schema
- Query
Answer: Query. The Query type defines the fields clients can read/fetch.
Which type is used for operations that CHANGE data, like addUser?
- Mutation
- Query
- Fragment
- Scalar
Answer: Mutation. The Mutation type defines write operations such as adding a user.
What does the type notation [User!]! describe?
- A single optional user
- A non-null list of non-null Users
- A string of users
- An empty list
Answer: A non-null list of non-null Users. [User!]! is a required list whose every element is a non-null User.
Which library is shown for building a real GraphQL server in the lesson?
- Express GraphQL Classic
- GraphiQL only
- Apollo Server (@apollo/server)
- Mongoose
Answer: Apollo Server (@apollo/server). The lesson uses @apollo/server with startStandaloneServer.
How does a client avoid over-fetching with GraphQL?
- It downloads the whole database
- The server always sends every field
- It uses multiple endpoints
- It lists only the fields it needs in the query
Answer: It lists only the fields it needs in the query. By naming just the fields it wants, the client gets exactly that data.
In Apollo Server, how are resolvers organized?
- Grouped by type, e.g. Query and Mutation
- In a single flat array
- Inside the database
- As URL routes
Answer: Grouped by type, e.g. Query and Mutation. Resolvers are grouped under Query, Mutation, etc., one function per field.
What is the ID type's role in the User schema?
- It stores an image
- It is a unique identifier for the object
- It is the user's password
- It is a timestamp
Answer: It is a unique identifier for the object. ID is a scalar serving as a unique identifier for an object.
Continue this course
- Previous: WebSockets
- Next: SQL with Sequelize