Node.js GraphQL
GraphQL is a query language for APIs. Clients ask for exactly the fields they need, and the server returns exactly that data. One address serves every request. Many modern apps use GraphQL for flexible data loading.
The Restaurant Menu Idea
A REST API works like a set meal. You order the lunch box and receive every item inside, even food you skip. GraphQL works like an a la carte menu. You pick only the dishes you want.
REST: GET /users/5 --> { id, name, email, phone, address, ... }
(all fields, needed or not)
GraphQL: { user(id: 5) { name } }
--> { "user": { "name": "Asha" } }
(only the name)
REST and GraphQL Compared
+----------------+----------------------+---------------------+ | Topic | REST | GraphQL | +----------------+----------------------+---------------------+ | Addresses | Many | One (/graphql) | | Response shape | Fixed by server | Chosen by client | | Related data | Several requests | One request | +----------------+----------------------+---------------------+
Core Ideas
- Schema describes the data types and available operations.
- Query reads data.
- Mutation changes data.
- Resolver is a function that fetches the data for a field.
Set Up a Server
Apollo Server is a popular GraphQL library.
npm install @apollo/server graphqlWrite the Schema
The schema acts as a contract between client and server. Each field has a type. An exclamation mark means the value is required.
const typeDefs = `#graphql
type Book {
id: ID!
title: String!
author: String!
year: Int
}
type Query {
books: [Book!]!
book(id: ID!): Book
}
type Mutation {
addBook(title: String!, author: String!, year: Int): Book!
}
`;Write the Resolvers
let books = [
{ id: "1", title: "Node Basics", author: "Asha", year: 2022 },
{ id: "2", title: "Async Magic", author: "Ravi", year: 2023 }
];
const resolvers = {
Query: {
books: () => books,
book: (parent, args) => books.find((b) => b.id === args.id)
},
Mutation: {
addBook: (parent, args) => {
const book = { id: String(books.length + 1), ...args };
books.push(book);
return book;
}
}
};Start the Server
const { ApolloServer } = require("@apollo/server");
const { startStandaloneServer } = require("@apollo/server/standalone");
const server = new ApolloServer({ typeDefs, resolvers });
startStandaloneServer(server, { listen: { port: 4000 } })
.then(({ url }) => console.log("Server ready at " + url));Open the address in a browser to use the built-in explorer.
How a Request Flows
Client query
{ book(id: "1") { title author } }
|
v
+---------------+ matches +------------------+
| Schema | ----------> | Query.book |
+---------------+ | resolver |
+------------------+
|
v
Data source (array, database)
|
v
Response: { "data": { "book": { "title": "Node Basics", "author": "Asha" } } }
Send Queries
Ask for a List
{
books {
title
year
}
}Ask with a Variable
query GetBook($id: ID!) {
book(id: $id) {
title
author
}
}Change Data with a Mutation
mutation {
addBook(title: "GraphQL Guide", author: "Meera", year: 2024) {
id
title
}
}Connect Related Data
GraphQL follows links between types in one request. Suppose each book belongs to an author with a list of books.
{
author(id: "1") {
name
books {
title
}
}
}A REST client needs two calls for this task. GraphQL needs one.
Handle Errors
GraphQL returns status 200 even for many errors. The response carries an errors list. Throw a clear error inside a resolver for bad input.
book: (parent, args) => {
const found = books.find((b) => b.id === args.id);
if (!found) {
throw new Error("Book not found");
}
return found;
}When to Choose GraphQL
- Mobile apps that need small responses.
- Screens that combine data from many sources.
- Teams with many different clients.
Simple APIs with few resources often work well with REST.
Practice Tasks
- Add a deleteBook mutation.
- Query only the titles of all books.
- Add a genre field to the schema and return it.
