The Core Difference
REST exposes fixed endpoints that return fixed data shapes. GraphQL exposes a single endpoint where clients specify exactly what data they need. This single distinction drives most of the practical trade-offs between them.
REST Example
// Two separate requests, each returning its full fixed shape
GET /api/users/42
GET /api/users/42/posts
{ "id": 42, "name": "Jane", "email": "jane@example.com", "createdAt": "2026-01-01" }
GraphQL Example
query {
user(id: 42) {
name
posts {
title
publishedAt
}
}
}
One request, and the client gets exactly name and each post’s title/publishedAt — nothing more, nothing less.
Setting Up a Simple GraphQL Server
const { ApolloServer, gql } = require('apollo-server');
const typeDefs = gql`
type Post {
id: ID!
title: String!
}
type User {
id: ID!
name: String!
posts: [Post!]!
}
type Query {
user(id: ID!): User
}
`;
const resolvers = {
Query: {
user: (_, { id }) => db.users.findById(id),
},
User: {
posts: (user) => db.posts.findByUserId(user.id),
},
};
const server = new ApolloServer({ typeDefs, resolvers });
server.listen().then(({ url }) => console.log(`Server ready at ${url}`));
Where REST Still Wins
- Simple CRUD APIs where over/under-fetching isn’t a real problem
- Strong HTTP caching semantics (ETags, Cache-Control) that GraphQL doesn’t get for free
- Simpler mental model and tooling for small teams and small APIs
Where GraphQL Wins
- Multiple client types (web, mobile, third-party) with very different data needs from the same backend
- Complex, deeply nested data where REST would require many round-trips or bespoke endpoints
- Rapidly evolving frontend requirements, since clients can request new field combinations without backend changes
Common GraphQL Pitfalls
- N+1 queries in resolvers — solve with a batching library like DataLoader.
- No built-in caching — requires deliberate client-side caching (Apollo Client, urql) since there’s no single resource URL to cache against.
- Overly permissive queries — implement query depth/complexity limits to prevent expensive, deeply nested queries from overwhelming your server.
Conclusion
Neither architecture is objectively better — REST remains the simpler default for straightforward APIs, while GraphQL earns its added complexity when you have genuinely varied clients and deeply nested data needs. Many real systems use both: REST for simple resource CRUD, GraphQL for complex, client-driven data fetching.