REST vs GraphQL: Differences and When to Use Each
REST vs GraphQL compared at the code level: how queries, caching, errors, security and tooling differ, and a clear framework for choosing on your next project.
REST exposes many resource URLs and uses standard HTTP methods and caching, which makes it simple, predictable and CDN-friendly. GraphQL exposes 1 endpoint where clients ask for exactly the fields they need, which cuts over-fetching and round trips for complex, nested data. Choose REST for public, cache-heavy or simple APIs, and GraphQL for data-rich apps with many different clients.
Photo: Unsplash
On this page
Your mobile app’s home screen needs a user’s name, their last 3 orders and the shipping status of each one. With a typical REST API, that might be 1 call for the user, 1 for the orders and 3 more for shipments. With GraphQL, it is 1 request that returns exactly those fields. So why does almost every public API still use REST?
Because each approach makes different trade-offs on caching, tooling, security and team workflow. This guide compares them with real code so you can make the call for your own project.
REST and GraphQL in one paragraph each
REST (Representational State Transfer) is an architectural style described in Roy Fielding’s 2000 doctoral dissertation. A REST-style API models data as resources, each with its own URL (/users/42, /users/42/orders), and uses standard HTTP methods: GET to read, POST to create, PUT or PATCH to update, DELETE to remove. The server decides the shape of each response.
GraphQL is a query language and runtime for APIs that Facebook created in 2012 and open-sourced in 2015. It is now governed by the GraphQL Foundation under the Linux Foundation. A GraphQL API publishes a typed schema, serves everything from a single endpoint (usually /graphql) and lets the client decide exactly which fields to fetch.
The same request, side by side
Here is the home screen example in REST:
http
GET /users/42
GET /users/42/orders?limit=3
GET /orders/981/shipment
GET /orders/977/shipment
GET /orders/960/shipment
Each response returns the full representation the server defines, including fields the screen does not need. You can reduce the calls with custom endpoints (/users/42/home-summary) or query parameters like ?include=shipment&fields=id,status, but those are conventions you design and maintain yourself.
And in GraphQL:
graphql
query HomeScreen {
user(id: "42") {
name
orders(last: 3) {
id
total
shipment {
status
eta
}
}
}
}
The response mirrors the query shape:
json
{
"data": {
"user": {
"name": "Dana",
"orders": [
{ "id": "981", "total": 42.5, "shipment": { "status": "IN_TRANSIT", "eta": "2026-10-14" } }
]
}
}
}
GitHub, which offers both a REST and a GraphQL API, makes the same point in its documentation: a single GraphQL query can replace several REST requests and return only the data you asked for, while REST responses have a fixed structure that may include fields you do not need. GitHub also notes the flip side: some simple operations, like creating an issue, are more direct in REST.
Key differences at a glance
| Area | REST | GraphQL |
|---|---|---|
| Endpoints | Many, one per resource | Usually 1 (/graphql) |
| Response shape | Defined by the server | Defined by the client query |
| Over- and under-fetching | Common without custom endpoints | Largely avoided |
| HTTP caching | Native: URLs, Cache-Control, ETags, CDNs |
Harder; needs GET queries, persisted queries or client caches |
| Errors | HTTP status codes (404, 401, 500) | errors array in the body; partial success possible with a 2xx status |
| Schema and types | Optional (OpenAPI is common) | Required, built in |
| Versioning | Often URL or header versions (/v2/) |
Usually evolve the schema and deprecate fields |
| Real-time | Webhooks, server-sent events, WebSockets added separately | Subscriptions in the spec, transport varies |
| File uploads | Simple multipart requests | Not in the core spec; needs extensions or separate endpoints |
| Learning curve | Low; uses familiar HTTP | Moderate; schema design and resolvers |
| Security focus | Per-endpoint auth, rate limits by request | Query depth and cost limits, field-level auth |
Key takeaway: REST pushes complexity to the client (more calls, extra data) but keeps the server and caching simple. GraphQL pushes complexity to the server (resolvers, query limits, caching) but makes the client’s job much easier.
Caching: REST’s biggest advantage
HTTP caching is built around URLs and methods. With REST, a response to GET /products/123 can carry Cache-Control and ETag headers defined in the HTTP caching standard (RFC 9111), and every browser, proxy and CDN between client and server knows what to do with it:
http
HTTP/1.1 200 OK
Cache-Control: public, max-age=300
ETag: "a1b2c3"
GraphQL’s own documentation explains why this is harder for GraphQL: in endpoint-based APIs, the URL acts as a globally unique identifier for caching, and GraphQL has no equivalent because most requests go to the same endpoint as POST bodies. The common fixes:
- Client-side normalized caches (for example in Apollo Client or Relay) that store objects by a globally unique
idfield. GraphQL.org recommends exposing such an ID for exactly this reason. - GET for queries. The GraphQL-over-HTTP guidance allows GET for query operations (never mutations), which lets HTTP and CDN caching work.
- Persisted queries. Long query strings can exceed URL limits, so clients send a short hash or ID of a query stored on the server. GraphQL.org names persisted documents, automatic persisted queries and trusted documents as techniques.
If your API serves mostly public, cacheable content to anonymous users, REST’s caching story is hard to beat.
Errors, versioning and tooling
Errors. REST uses HTTP status codes: a missing order is a 404, an expired token is a 401. GraphQL can return partial results: if 1 nested field fails, the response can still be a 2xx with data for the parts that worked and an errors array describing the failure. That is powerful for complex screens, but your monitoring must inspect response bodies, not just status codes.
Versioning. REST APIs commonly version with /v1/ and /v2/. GraphQL APIs usually evolve a single schema: add new fields freely and mark old ones with @deprecated until clients stop using them. Because clients request fields explicitly, you can see which deprecated fields are still in use.
Tooling.
- REST: OpenAPI (formerly Swagger) specs for documentation, client generation and contract testing; tools like Postman or curl; easy debugging in browser dev tools.
- GraphQL: the schema is the contract. Introspection powers autocomplete in tools like GraphiQL, typed client code generation and schema linting. You need a server library and resolver design, and you must watch for the “N+1” problem, where resolving a list triggers 1 database query per item. Batching tools such as DataLoader are the standard fix.
Security considerations
Both styles need authentication, authorization, input validation and rate limiting. GraphQL adds a few specific risks because clients write the queries. The OWASP GraphQL Cheat Sheet recommends:
- Limit query depth so deeply nested queries cannot exhaust the server.
- Limit amounts requested per field and enforce pagination.
- Add query cost or complexity analysis where needed.
- Disable introspection and GraphiQL in production for public APIs, or restrict them to approved roles.
- Limit batching to prevent brute-force attempts packed into a single request.
- Set application-level timeouts and GraphQL-aware rate limits, since 1 HTTP request can be very expensive.
- Authorize at the field and object level, not just at the endpoint.
With REST, each endpoint has a predictable cost, so rate limiting by request count works reasonably well. With GraphQL, you need to measure query cost.
When to use REST vs GraphQL
Choose REST when:
- You are building a public API for third-party developers who expect familiar HTTP conventions.
- Responses are highly cacheable and served through a CDN.
- The API is simple CRUD over a handful of resources.
- You need simple file uploads and downloads.
- You are sending server-to-server events; webhooks are a REST-style pattern.
Choose GraphQL when:
- You have multiple clients (web, iOS, Android, partners) that need different slices of the same data.
- Screens need deeply nested, related data that would take many REST calls.
- Mobile performance matters and you want fewer round trips and smaller payloads.
- You want a single typed contract across many backend services. GraphQL often acts as a layer in front of several services, which can pair well with microservices.
- Frontend teams need to move fast without waiting for new backend endpoints.
Use both when it makes sense. Many companies run GraphQL for their own apps and REST for public integrations and webhooks.
Where to start on your next project
- List your clients and screens. If 1 web app reads simple resources, REST is likely enough.
- Map your data. Draw the entities and relationships. Deep, interconnected graphs favor GraphQL; flat resources favor REST.
- Check caching needs. Public, high-traffic, cacheable content leans REST.
- Assess your team. GraphQL schema design, resolvers, DataLoader and query limits are new skills. REST with OpenAPI is familiar to most developers.
- Prototype 1 real screen both ways and compare request count, payload size and code complexity.
- Decide on guardrails up front: OpenAPI plus rate limits for REST, or depth limits, cost analysis and persisted queries for GraphQL.
Whatever you pick, your database choices still matter underneath; see SQL vs NoSQL for that decision.
Conclusion
REST and GraphQL solve the same problem from opposite ends. REST keeps the server simple and leans on decades of HTTP caching and tooling, at the cost of extra calls and fixed responses. GraphQL gives clients exactly the data they need in 1 request, at the cost of more server-side work on caching, performance and security. Pick REST for public, cacheable and simple APIs, GraphQL for data-rich apps with many clients, and do not be afraid to run both.
Sources
- GraphQL.org: Caching — why URL-based HTTP caching does not map to GraphQL and the role of global IDs
- GraphQL.org: Serving over HTTP — single endpoint, GET vs POST, status codes and persisted queries
- GitHub Docs: Comparing GitHub’s REST API and GraphQL API — request counts and response shapes in a real API offering both
- OWASP GraphQL Cheat Sheet — depth limits, cost analysis, introspection, batching and rate limiting
- IETF RFC 9111: HTTP Caching — the HTTP caching standard REST APIs rely on
- GraphQL Foundation — governance of GraphQL under the Linux Foundation
Frequently asked questions
Is GraphQL better than REST?
Neither is better in every case. GraphQL is better for apps with many clients and complex, nested data, because clients fetch exactly what they need in 1 request. REST is better for public APIs, simple resources and cache-heavy content, because it uses standard HTTP methods, status codes and caching. Many teams use both for different jobs.
Is GraphQL faster than REST?
GraphQL can feel faster for clients because it replaces several REST calls with 1 request and returns smaller payloads. But a single complex query can be slower on the server, and GraphQL is harder to cache at the HTTP and CDN level. For simple, cacheable reads, a well-designed REST API is often faster end to end.
Does GraphQL replace REST?
No. GraphQL is an alternative for some use cases, not a replacement. Most public APIs, webhooks and file-transfer endpoints still use REST-style HTTP. A common pattern is GraphQL for a company's own web and mobile apps, sitting in front of REST or internal services, with REST kept for public integrations.
Can you cache GraphQL responses?
Yes, but it takes more work than REST. Options include client-side normalized caches keyed by a globally unique `id`, sending queries as GET requests so HTTP and CDN caches can store them, and persisted queries that replace long query text with a short identifier. Server-side response or field caches are also common.
What is over-fetching in REST?
Over-fetching is when an API returns more data than the client needs, such as a full user object when a screen only needs a name. Under-fetching is the opposite, where 1 call is not enough and the client must make several. GraphQL avoids both by letting the client specify exact fields, while REST APIs address them with custom endpoints or field parameters.
Is GraphQL secure?
GraphQL can be as secure as REST, but it needs specific protections because clients write the queries. The OWASP GraphQL Cheat Sheet recommends query depth and amount limits, cost analysis, disabling introspection in production for public APIs, batching limits, timeouts, GraphQL-aware rate limiting and field-level authorization, on top of normal authentication and input validation.
Should a beginner learn REST or GraphQL first?
Learn REST first. It teaches HTTP methods, status codes, headers and caching, which are foundations of all web development, including GraphQL over HTTP. Once you are comfortable building and calling REST APIs, GraphQL's schemas, queries and resolvers are much easier to understand, and you will appreciate the trade-offs between the 2 approaches.
Was this guide useful
No reviews yet. Share your experience and help other readers.