REST vs GraphQL in practice
I have built APIs with both REST and GraphQL. The internet loves to frame this as a debate, but in practice the choice is straightforward: it depends on your client's data needs.
When REST wins
REST is the default for a reason. It is simple, well-understood, and every developer knows how to work with it. For most APIs, REST endpoints that return well-shaped JSON responses are all you need.
// Clean, predictable, easy to cache
GET /api/users/123
GET /api/users/123/posts
GET /api/posts?category=tech&limit=10REST shines when:
- Your data model maps naturally to resources (users, posts, products)
- You have a small number of clients with predictable data needs
- You want HTTP caching to work out of the box
- Your team is small and does not want to maintain a schema
When GraphQL wins
GraphQL solves a real problem: when different clients need different shapes of the same data. A mobile app might need a user's name and avatar. A dashboard might need the same user's name, email, role, and last 10 actions. A report might need aggregate stats.
With REST, you either create multiple endpoints, add query parameters for field selection, or return everything and let the client ignore what it does not need. GraphQL lets the client ask for exactly what it wants:
# Mobile app
query {
user(id: "123") {
name
avatarUrl
}
}
# Dashboard
query {
user(id: "123") {
name
email
role
recentActions(limit: 10) {
type
timestamp
}
}
}One endpoint, one schema, different responses based on what the client asks for.
The hidden costs of GraphQL
Complexity. You need a schema definition, resolvers, a query execution engine, and often a code generation step for type safety. For a simple CRUD API, this is significant overhead.
Caching. HTTP caching does not work naturally with GraphQL because everything is a POST to a single endpoint. You need application-level caching (Apollo Client, urql) or persisted queries.
N+1 queries. A naive GraphQL resolver for "users with their posts" will execute one database query per user to fetch their posts. You need DataLoader or similar batching to avoid this.
Security. Clients can write deeply nested queries that consume significant server resources. You need query depth limiting, complexity analysis, or persisted queries to prevent abuse.
What I use in practice
For most of my projects, REST is the answer. The simplicity wins. I use REST for Landbound's API because the data access patterns are predictable and I do not need multiple clients with different data needs.
I reach for GraphQL when I am building a product with multiple frontends (web, mobile, third-party integrations) that need different views of the same data. The upfront investment in the schema pays off when you stop maintaining separate endpoints for each client.
The pragmatic approach
Start with REST. If you find yourself creating increasingly specific endpoints for different clients or adding complex query parameters for field selection, consider GraphQL. Do not adopt it because it is trendy. Adopt it because your data access patterns demand it.
Sources
Related posts
Why I built Omnibase: a universal database MCP server
I got tired of copy-pasting query results between DataGrip and AI agents. So I built an MCP server that gives AI agents secure, direct access to any database.
Delta libraries: how diffing works and which library to use
What delta libraries do, how diff algorithms work under the hood, and a practical comparison of the most popular options in the JavaScript ecosystem.
Offline-first apps: harder than it sounds
Building apps that work without internet is one of those things that seems straightforward until you actually try it. Here is what makes it hard and how to approach it.
Enjoying the blog? Subscribe via RSS to get new posts in your reader.
Subscribe via RSS