REST vs GraphQL: Tradeoffs in Real Projects
Use REST for most projects. It is simpler to build, easier to cache, and every developer understands it. Use GraphQL when your frontend needs to fetch complex, nested data from multiple resources in a single request, and you have the team capacity to maintain a schema and resolver layer. REST is the safer default. GraphQL solves a real problem, but only for projects that actually have that problem.
A worked example: loading a user profile page
Imagine a profile page that shows a user's name, their five most recent orders, and the items in each order. Here is how both approaches handle it.
REST approach (multiple requests):
// Request 1: Get the user
GET /api/users/42
// Response: { id: 42, name: "Jane", email: "jane@example.com", avatar: "..." }
// Request 2: Get their orders
GET /api/users/42/orders?limit=5
// Response: [{ id: 101, total: 2500, date: "2026-07-15" }, ...]
// Request 3-7: Get items for each order
GET /api/orders/101/items
GET /api/orders/102/items
// ... three more requestsThat is 3 to 7 HTTP requests to load one page. Each request adds latency, especially on mobile networks in Kenya where round-trip times can be 100ms or more.
GraphQL approach (one request):
// Single request
POST /graphql
{
user(id: 42) {
name
email
orders(limit: 5) {
id
total
date
items {
name
quantity
price
}
}
}
}One request, one response, exactly the data the page needs. No over-fetching (getting fields you do not display), no under-fetching (needing additional requests).
This is GraphQL's core strength. If your pages routinely need data from multiple related resources, GraphQL reduces network round trips.
Why REST is still the default
The profile page example makes GraphQL look like the obvious choice. But most API endpoints are simpler than that example, and REST has significant advantages:
- Caching: REST endpoints map to URLs. URLs are cacheable by browsers, CDNs, and reverse proxies.
GET /api/products/42can be cached at every layer. GraphQL sends all requests as POST to a single endpoint, which makes HTTP caching nearly impossible without custom tooling. - Simplicity: A REST API is a set of URL endpoints that return JSON. Every developer understands this pattern. GraphQL requires learning a query language, a schema definition language, and a resolver architecture. The learning curve is real.
- Error handling: REST uses HTTP status codes. 404 means not found, 401 means unauthorized, 500 means server error. GraphQL always returns 200 and puts errors in the response body, which makes monitoring and debugging less intuitive.
- File uploads: REST handles file uploads with multipart form data. GraphQL does not have a built-in file upload mechanism. You end up using REST for uploads alongside your GraphQL API.
- Tooling: REST APIs work with curl, Postman, browser dev tools, and every HTTP client library. GraphQL needs specialized tools like GraphiQL or Apollo Explorer.
When GraphQL genuinely helps
- Multiple frontend clients: If a web app, mobile app, and admin dashboard all consume the same API but need different subsets of data, GraphQL lets each client request exactly what it needs without building separate endpoints for each.
- Deeply nested data: When your data model has many levels of relationships (user, orders, items, reviews, seller, seller ratings), GraphQL fetches the entire tree in one request.
- Rapid frontend iteration: Frontend developers can add or remove fields from their queries without waiting for backend developers to modify endpoints. This reduces coordination overhead in larger teams.
- Strong typing: The GraphQL schema serves as a contract between frontend and backend. TypeScript types can be auto-generated from the schema, giving end-to-end type safety.
REST solutions for GraphQL problems
Many of the problems GraphQL solves can be addressed within REST:
Over-fetching: Add a fields query parameter to let clients specify which fields they want:
GET /api/users/42?fields=name,emailUnder-fetching (multiple requests): Create a composite endpoint that returns related data:
GET /api/users/42/profile
// Returns user + orders + items in one responseMultiple clients needing different data: Use the Backend for Frontend (BFF) pattern. Each client type gets its own API layer that aggregates backend services.
These solutions add complexity to your REST API, but they avoid the complexity of maintaining a GraphQL schema, resolvers, and the N+1 query problem that plagues poorly optimized GraphQL servers.
The verdict
Start with REST. It covers 90% of projects well. Every tutorial, every library, every developer on your team understands it. The learning curve is zero.
Consider GraphQL when: you have multiple frontend clients consuming the same API, your data is deeply nested and relational, and your team has the capacity to invest in schema design and query optimization.
Do not adopt GraphQL because it is trendy or because a blog post showed a clean query syntax. Adopt it when you feel the pain that REST causes in your specific project, and when that pain outweighs the operational cost of running a GraphQL server.
Frequently Asked Questions
- Can I use both REST and GraphQL in one project?
- Yes, and many teams do. A common pattern is REST for simple CRUD operations and file uploads, with a GraphQL endpoint for complex data fetching on specific pages. There is no rule that says you must pick one for everything.
- Is GraphQL faster than REST?
- Not inherently. A single GraphQL query can be faster than multiple REST requests because it reduces round trips. But the GraphQL server still needs to resolve all the requested data, which can result in more database queries than a well-designed REST endpoint. Performance depends on implementation, not architecture.
- What about tRPC as an alternative?
- tRPC gives you end-to-end type safety between a TypeScript backend and TypeScript frontend without a schema definition language. It is a strong choice for full-stack TypeScript applications (like Next.js projects) where both frontend and backend live in the same codebase. It solves many of the same problems as GraphQL with less overhead, but only works when both client and server are TypeScript.
Ready to build real-world apps?
Join the McTaba Labs full-stack marathon. Ship 8 production apps with M-Pesa, USSD, and WhatsApp integrations, and get career support until placement.
See Programs