Why is REST API No Longer the “Only Way”?
Looking back at my early days in web development, REST API was truly the “gold standard”. Every time I needed new data, I’d diligently create a new endpoint: /users, /posts, /comments. Everything was fine until the project grew. Frontend developers started complaining: “Hey, I just need the User’s name, why is the API returning email, address, and date of birth? It’s weighing down the client,” or “To display one post, I have to call 3-4 different APIs just to gather enough data.”
That’s when I really felt the pain of Over-fetching and Under-fetching. GraphQL was born to sweep away these troubles. Instead of the backend dictating the data structure, GraphQL empowers the Client: you ask for exactly what you need, and the server returns just that.
Real-world Differences: Set Meals vs. Buffet
REST API: Eating from a Fixed Menu
REST is like going to a local eatery. You order a “Chicken Rice Set”, and the owner brings out the whole set: rice, chicken, soup, and pickles. Even if you hate pickles, they’re still there on your plate (Over-fetching). Want an extra pork chop? You have to pay and wait to order another plate (Under-fetching).
GraphQL: A Data Buffet
GraphQL is completely different—it’s like a buffet. You take your plate (Query) and pick only the dishes you crave. Want name and avatar? Done. Want to get the title of all posts published by that user in the same request? Just put it on your plate and you’re good to go.
Pros and Cons in Real Projects
In a Dashboard project I worked on, switching to GraphQL helped reduce the number of requests from 8 down to exactly 1 per page load. However, let’s not over-hype it.
Why use it?
- Bandwidth Savings: Payload is significantly reduced, which is extremely helpful for users on unstable 4G/5G connections.
- Strict Contracts: The Schema acts as a commitment between Frontend and Backend. If the data type is wrong, the system “screams” immediately.
- Single Entry Point: You don’t need to remember dozens of complex URLs; everything revolves around a single endpoint:
/graphql.
Barriers to consider:
- Caching Headaches: Since POST is used for all requests, leveraging the browser’s native HTTP Caching is much harder than with REST.
- New Mindset: Your team needs time to get used to thinking in terms of a Graph rather than traditional flat tables.
Strategic Tool: Apollo Server
There are quite a few libraries for building GraphQL, but Apollo Server is always my top choice. It’s not only stable but also comes with the excellent Apollo Sandbox. This tool helps you test APIs visually, clearly seeing the data structure instead of squinting at raw JSON lines.
Let’s get to coding!
Before we start, make sure you have Node.js installed. We’ll build a practical example: managing Books and Authors.
Step 1: Setting the Foundation
mkdir node-graphql-demo
cd node-graphql-demo
npm init -y
npm install apollo-server graphql
Step 2: Drawing the Data Portrait (Schema)
Create an index.js file. This is where we define the data structure using SDL (Schema Definition Language):
const { ApolloServer, gql } = require('apollo-server');
const typeDefs = gql`
type Book {
id: ID!
title: String
author: Author
}
type Author {
id: ID!
name: String
books: [Book]
}
type Query {
books: [Book]
book(id: ID!): Book
}
type Mutation {
addBook(title: String!, authorId: ID!): Book
}
`;
Step 3: Implementing the Logic (Resolvers)
If the Schema is the skeleton, the Resolver is the brain. It decides where the data comes from (Database, JSON file, or a third-party API).
const authors = [
{ id: '1', name: 'Nam Cao' },
{ id: '2', name: 'To Hoai' },
];
const books = [
{ id: '1', title: 'Old Man Hac', authorId: '1' },
{ id: '2', title: 'Diary of a Cricket', authorId: '2' },
];
const resolvers = {
Query: {
books: () => books,
book: (_, args) => books.find(b => b.id === args.id),
},
Book: {
author: (parent) => authors.find(a => a.id === parent.authorId),
},
Mutation: {
addBook: (_, { title, authorId }) => {
const newBook = { id: String(books.length + 1), title, authorId };
books.push(newBook);
return newBook;
}
}
};
Step 4: Activating the Server
const server = new ApolloServer({ typeDefs, resolvers });
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at: ${url}`);
});
Verifying the Results
Run node index.js and open your browser to the provided URL. Try running this Query:
query {
books {
title
author {
name
}
}
}
You’ll see the results exactly as expected: book titles along with author names in a heartbeat. The great thing is: if you omit the author field, the server automatically skips the author search logic, saving significant processing resources.
Hard-learned Lessons from Real Projects
My biggest mistake in the past was stuffing all Schemas into a single schema.graphql file. When the project hit 50+ entities, that file became a chaotic mess. Split them into modules from the start (User, Product, Order…).
One more minor note: GraphQL always returns a 200 status code. Don’t just check the HTTP status like you do with REST. Get into the habit of checking the errors array in the returned data for more accurate error handling.
I hope this article helps you feel less “hesitant” about GraphQL. Once you master the Schema mindset, you’ll find building APIs much more interesting and flexible.

