GraphQL vs REST: A Duel You Should Have Started Yesterday
\n\nFamiliar situation: you're writing a new project in JavaScript (or Node.js), and suddenly the question arises — what should you use for frontend-backend communication? Classic REST or trendy GraphQL? The debates on this topic never subside, and every developer has their own "ironclad" opinion.
\n\nLet's be honest: REST is an old, reliable friend that never lets you down. GraphQL is a bold newcomer that promises to solve all your problems. But how do you choose? I'll show you not just dry comparison tables, but real combat examples in JavaScript. You'll see what the code looks like, where the pitfalls arise, and most importantly — when to use what.
\n\nBy the way, if you're developing in Python (and this site is about Python, don't forget), the principles of choosing between these technologies are very similar. But today we're talking about JavaScript — the language that rules both the client and the server.
\n\nQuick Primer: REST and GraphQL in 60 Seconds
\n\nREST (Representational State Transfer)
\nThis is an architectural style where each resource (user, post, comment) has its own URL. You make a GET request to /api/users/1 — you get the user. Want their posts? Go to /api/users/1/posts. Everything is simple, predictable, but... sometimes too verbose.
GraphQL
\nThis is a query language and runtime environment. You have one endpoint (usually /graphql), and you decide for yourself what data you want to receive. No over-fetching (when 50 fields come but you need 3) and no under-fetching (when you need to make 5 requests to assemble a page).
Combat Comparison: Writing Code in JavaScript
\n\nLet's imagine a typical task: you need to display a user profile on a page with their last three posts. Let's compare both approaches.
\n\nREST API Example on Express (Node.js)
\n\n// REST server on Express\nconst express = require('express');\nconst app = express();\n\napp.get('/api/users/:id', (req, res) => {\n // Return ALL user data\n const user = users.find(u => u.id === req.params.id);\n res.json(user); // { id, name, email, phone, address, ... }\n});\n\napp.get('/api/users/:id/posts', (req, res) => {\n // Return ALL user posts\n const posts = posts.filter(p => p.userId === req.params.id);\n res.json(posts); // [{ id, title, body, createdAt, ... }]\n});\n\n// Client code (frontend using fetch)\nasync function loadProfile(userId) {\n // First request — get the user (lots of extra data comes)\n const userResponse = await fetch(`/api/users/${userId}`);\n const user = await userResponse.json();\n \n // Second request — get the posts (again extra data)\n const postsResponse = await fetch(`/api/users/${userId}/posts`);\n const allPosts = await postsResponse.json();\n \n // Take only the 3 latest posts\n const latestPosts = allPosts.slice(0, 3);\n \n return { user, posts: latestPosts };\n}\n\nThe problem is obvious: we got a lot of extra data (over-fetching). Why do we need email, phone, address if we're only showing the name and avatar? And there could be 100 posts, but we only need 3.
\n\nGraphQL Example on Apollo Server (Node.js)
\n\n// GraphQL schema\nconst typeDefs = `\n type User {\n id: ID!\n name: String\n avatar: String\n posts(limit: Int): [Post]\n }\n\n type Post {\n id: ID!\n title: String\n body: String\n }\n\n type Query {\n user(id: ID!): User\n }\n`;\n\n// Resolvers (data fetching logic)\nconst resolvers = {\n Query: {\n user: (parent, { id }) => users.find(u => u.id === id),\n },\n User: {\n posts: (parent, { limit }) => {\n let result = posts.filter(p => p.userId === parent.id);\n if (limit) result = result.slice(0, limit);\n