GraphQL vs REST is mostly a question about who calls your API, not which technology is better. Pick for the clients you have and the team you can hire, then check what each style makes you build. This guide covers both, with the caching, status code, rate limit and security differences that most comparisons skip. Teams weighing the hiring side can start from Hire developers by tech stack: rates, vetting and interview guides.
What each style actually is
REST (or REST-style HTTP APIs) models your system as resources with URLs, and uses HTTP methods and status codes to say what you want done with them. GraphQL is a typed query language served over one endpoint. The client describes the fields it wants, and the server's schema decides what's allowed. The GraphQL Foundation, which Facebook and others created in 2019 and the Linux Foundation hosts, exists to support the continued evolution of the spec.
The same data, for illustration only:
GET /users/42/orders?limit=3 HTTP/1.1
Host: api.example.com
Accept: application/jsonquery {
user(id: "42") {
name
orders(first: 3) {
id
total
}
}
}The REST call returns whatever the server's orders representation contains. The GraphQL query returns exactly name, id and total, in one round trip, because the client named them.
What REST gives you for free
REST sits on HTTP, so every proxy, CDN, browser and monitoring tool already understands it. Each resource has a URL, and that URL is its identity.
What GraphQL gives you instead
GraphQL moves identity and shape into the schema. You get field selection, one typed contract and tooling built on that contract. You also take on work that HTTP used to do for you, which the rest of this article is about.
Who calls your API: clients decide more than technology
If you have one first-party client, say your own web app and mobile app, GraphQL's main benefit is that frontend developers can change what they fetch without waiting for a new endpoint. If you have many third-party clients you can't coordinate with, REST's plain HTTP semantics are easier for strangers to adopt and for intermediaries to cache.
That's why a split is common: REST for the public API, GraphQL behind your own UI as a backend-for-frontend layer. Treat that as judgment, not a rule, because it means running two API surfaces. If your frontend is Next.js, where data fetching moves to the server, a thin server-side layer may be all the aggregation you need.
REST vs GraphQL isn't binary, either. Nothing stops a GraphQL resolver from calling REST services underneath.
What happens to caching, errors and rate limits
Caching and HTTP semantics
"GraphQL is hard to cache" is repeated everywhere and rarely explained. The mechanism is HTTP. GET is safe and idempotent under RFC 9110 and is a cacheable method, so caches can store and reuse its responses. POST responses, per section 9.3.3 of the same RFC, aren't cacheable unless the response carries explicit freshness information such as Cache-Control or Expires.
GraphQL servers must handle POST for queries and mutations, and may also accept GET for query operations. GET is allowed only for queries, never mutations. The spec notes a client may use GET to make HTTP caching or CDN edge caching possible. Servers often pair that with persisted documents (also called automatic persisted queries or trusted documents), where the server stores identified documents so a short ID replaces the long query text.
Identity is the other half. In an endpoint-based API, clients can use HTTP caching to avoid refetching and to tell when two resources are the same. GraphQL has no URL-like primitive for that, so the best practice is to expose a globally unique identifier on your objects and have clients cache by it.
Status codes
GraphQL over HTTP doesn't always return 200. For requests that fail validation, the server will typically send a 400, though some legacy servers return 2xx when the application/json media type is used. When the response has non-null data, the server should respond with a 2xx even if it includes errors, because HTTP has no status code for partial success.
In practice, your monitoring can't assume every failure shows up as a 5xx or 4xx (servers using the application/graphql-response+json media type do return 4xx or 5xx when a valid request fails to execute). You have to alert on the errors array too. REST's status codes are coarse, but every tool reads them.
Rate limits
A request-count limit doesn't fit GraphQL, because one request can ask for ten rows or ten thousand. GitHub runs both APIs, so it's a clean worked example. The REST API limit for an authenticated user is 5,000 requests per hour. The GraphQL API limit is 5,000 points per hour per user (10,000 for GitHub Enterprise Cloud members), where a query's cost depends on how many requests its connections would need, and a single call can't request more than 500,000 total nodes.
| Concern | REST | GraphQL |
|---|---|---|
| HTTP caching | GET responses cacheable by default semantics | POST not cacheable without Cache-Control or Expires; GET and persisted documents restore it |
| Object identity | The URL | A unique ID you must expose |
| Validation failures | 4xx status codes | Typically 400 |
| Partial failures | Usually a single status | 2xx with data and errors |
| Rate limit unit (GitHub) | Requests per hour | Points per hour, plus a node cap |
What you must build and defend
GraphQL hands the client a lot of power, so the server has to bound it.
The first problem is N+1. A resolver that loads a list and then makes one database call per item produces what GraphQL's performance guide calls the N+1 problem, where one initial request leads to N subsequent ones. The usual fix is batching with a tool like Facebook's DataLoader.
The second is abuse. The security guide covers, among other controls, depth limiting, query complexity analysis (a server may reject a request outright), trusted documents so the server only executes known document IDs, pagination limits, masked errors, and limiting introspection outside development for APIs that serve only first-party clients. Each control needs someone to design, implement and test it. A REST endpoint with a fixed response shape has a narrower surface by construction, though you still need authentication, authorisation and sensible pagination.
Versioning goes the other way. GraphQL's best-practice guidance is to evolve the schema over time without versions. REST teams usually version URIs or headers instead; how API versioning and deprecation work covers that side.
A decision rule
This is judgment, not a spec. Lean REST when:
- Third parties consume the API and you can't coordinate releases with them.
- You depend on CDN and HTTP caching and want it without extra design.
- The domain is mostly simple CRUD on well-defined resources.
Lean GraphQL when:
- You control several first-party clients with different data needs.
- Frontend teams are blocked on backend endpoint changes.
- You have people who will own cost limits, persisted documents and resolver performance.
If you can't name who owns that last item, choose REST. A mixed answer is fine.
What this means for your backend hiring
The API style you commit to decides what your next backend hire has to be good at. GraphQL adds a schema, resolvers and a security surface on top of the service itself. REST asks for something different: discipline with HTTP semantics, meaning correct methods, status codes and cache headers.
In an interview, make candidates show it. For GraphQL, ask how they'd find and fix an N+1 problem, how they'd stop a client from requesting a deeply nested query, and what they'd cache and where. For REST, ask what makes an endpoint safely cacheable, which methods are idempotent, and how they'd version a breaking change. Our Node.js guide describes the backend API engineer, working with Express, Fastify or NestJS, as the person building REST or GraphQL APIs.
If you're deciding this quarter, write down who owns the GraphQL controls before you adopt it. Then use what seniority means for backend hires to build the interview loop that tests them.
Hiring Node.js developers through HighCircl
HighCircl matches companies with vetted remote engineers, and Node.js is one of the stacks it covers. Vetting has four stages run by senior engineers, and around 1 in 10 applicants pass. You get a shortlist of three to five candidates within 72 hours. Rates run €45-105/hr ($50-115/hr), with a capped 20% margin shown separately, no subscription, no recruitment fee and no minimum hours. Engineers are based in seven European countries. See how to hire Node.js developers through HighCircl.
FAQ
Is GraphQL faster than REST?
It depends on the workload, and no primary benchmark settles it. GraphQL can cut round trips by letting a client fetch several related objects in one request. It can also push expensive joins and N+1 patterns onto the server, so poorly written resolvers can be slower. REST responses, being plain GETs, are easier to cache.
Can you use both?
Yes. A common setup is REST for the public API and GraphQL for your own UI, or GraphQL in front of existing REST services. Each extra surface needs its own monitoring, auth and documentation.
Does GraphQL replace REST?
No. They answer different questions. GitHub runs both side by side, with separate rate limit rules for each.
Does GraphQL need a different database?
No. GraphQL describes how clients query the API, not how data is stored. Resolvers can read from SQL, document stores or other services, and batching tools like DataLoader exist to stop those reads multiplying.
