A
AiTechWorlds
AiTechWorlds
| Aspect | REST | GraphQL |
|---|---|---|
| Invented | 2000 (Roy Fielding) | 2015 (Facebook/Meta) |
| Data shape | Server-defined per endpoint | Client-defined per query |
| Transport | HTTP (GET, POST, PUT, DELETE) | HTTP POST (usually) |
| Type system | None (OpenAPI is optional) | Built-in, required |
| Versioning | URL (/v1/, /v2/) | Schema evolution (no versions) |
| Caching | HTTP cache (GET requests) | Manual (POST isn't cached by default) |
GET /users → list all users
POST /users → create user
GET /users/42 → get user 42
PUT /users/42 → replace user 42
PATCH /users/42 → partial update user 42
DELETE /users/42 → delete user 42
GET /users/42/posts → user's posts (nested resource)GET /users/42
{
"id": 42,
"name": "Alice",
"email": "alice@example.com",
"role": "admin",
"created_at": "2024-01-15",
"preferences": { ... },
"billing": { ... }
}Over-fetching: client receives role, preferences, billing even if it only needed name and email.
Under-fetching: to get a user's posts, you need a second request to /users/42/posts.
const express = require('express');
const router = express.Router();
router.get('/users/:id', async (req, res) => {
const user = await db.users.findById(req.params.id);
if (!user) return res.status(404).json({ error: 'User not found' });
res.json(user);
});
router.post('/users', async (req, res) => {
const user = await db.users.create(req.body);
res.status(201).json(user);
});| Concept | Description |
|---|---|
| Schema | Typed definition of all data and operations |
| Query | Read data (like GET) |
| Mutation | Write data (like POST/PUT/DELETE) |
| Subscription | Real-time updates (WebSocket) |
| Resolver | Function that fetches data for a field |
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
content: String
author: User!
}
type Query {
user(id: ID!): User
users: [User!]!
posts(authorId: ID): [Post!]!
}
type Mutation {
createUser(name: String!, email: String!): User!
updateUser(id: ID!, name: String): User!
deleteUser(id: ID!): Boolean!
}# Only fetch name and email — nothing else
query GetUser {
user(id: "42") {
name
email
}
}
# Fetch user AND their posts in one request (no under-fetching)
query UserWithPosts {
user(id: "42") {
name
posts {
title
content
}
}
}mutation CreateUser {
createUser(name: "Bob", email: "bob@example.com") {
id
name
}
}const { ApolloServer, gql } = require('@apollo/server');
const typeDefs = gql`
type User { id: ID!, name: String! }
type Query { user(id: ID!): User }
`;
const resolvers = {
Query: {
user: async (_, { id }) => db.users.findById(id),
},
};
const server = new ApolloServer({ typeDefs, resolvers });| Scenario | REST | GraphQL |
|---|---|---|
| Simple CRUD | Excellent | Overkill |
| Mobile apps (bandwidth-sensitive) | Over-fetches | Ideal |
| Multiple data sources in one request | Multiple round trips | Single query |
| Public API for third parties | Better documented | Harder to explore |
| Real-time (subscriptions) | SSE or WebSocket required | Built-in subscriptions |
| File uploads | Native multipart | Requires workaround |
| Browser caching | HTTP cache works natively | Needs Apollo Client cache |
| Type safety | Optional (OpenAPI) | Built-in |
| Learning curve | Low | Medium |
Choose REST when:
Choose GraphQL when:
query {
posts { # 1 query
title
author { # N queries (one per post!)
name
}
}
}Fix: DataLoader (batching)
const DataLoader = require('dataloader');
const userLoader = new DataLoader(async (ids) => {
const users = await db.users.findByIds(ids);
return ids.map(id => users.find(u => u.id === id));
});
// Now all author fetches batch into one query
const resolvers = {
Post: {
author: (post) => userLoader.load(post.authorId)
}
};/users/42/posts/7/comments/3/likes — flatten with query params insteadDownload REST vs GraphQL: Complete Comparison
Get this note + 100s more free on Telegram
Join AiTechWorlds on Telegram and get daily AI tips, prompt engineering templates, coding resources, and exclusive content — 100% free!
No spam. Leave anytime.