APIer/Finale: REST vs GraphQL
CHAPTER · Comparison and decision guide

Finale: REST or GraphQL?

You have now seen both. This chapter teaches no new syntax. It teaches one judgment: which style fits which situation. Neither one replaces the other, and most teams that use GraphQL run REST endpoints as well.

§01

The comparison, row by row

The same blog data, served two ways. Open a row to read the full explanation — each one comes from a chapter you have already finished.

Before you read the table

Through this course, the same blog data was served twice. REST gave it endpoints such as /posts and /users/42/orders, and used the uniform interface and HTTP caching. GraphQL described the same data in one schema and let each client select the fields it wanted. The table below puts the two side by side. Read it as a list of trade-offs rather than a scoreboard: what decides the choice is your situation, not a total.

No clear winner — it depends

Many endpoints is not by itself a problem. Each URL is a place where you can cache, rate-limit, and log separately. But the list grows: every integrator wants a slightly different set of fields, so teams keep adding custom endpoints. That pressure is what pushed GitHub toward GraphQL. A single endpoint removes the routing work and moves the same complexity into the query language.

REST has the advantage on 4 rows, GraphQL on 3, and 4 rows have no clear winner. This is not a score to add up: only the rows your own situation touches should affect the decision.
§02

A decision guide: five questions

The hard part of a design meeting is not knowing the technology. It is stating the reason out loud.

Two other options belong on the table first. Neither is a variant of REST, and neither competes with GraphQL on the same ground — but both come up in real design meetings.

Another option · RPC
tRPC: types across one TypeScript codebase

RPC stands for remote procedure call. Instead of modeling resources or writing a query, the client calls a function on the server. tRPC applies that idea inside one TypeScript repository: the back end exports functions, and the front end receives their exact types with no schema file and no code generation. Change a signature and the front end stops compiling. Version 11 is stable, and it is widely used with Next.js.

The limit is equally clear: it only works where both sides are TypeScript, so it cannot serve a public API. It is also a different kind of thing from REST and GraphQL — calling functions, not describing data.

Another option · service to service
gRPC: calls between back-end services

gRPC comes from Google. The contract is written in Protocol Buffers (a binary serialization format), the calls run over HTTP/2, streaming is part of the design, and client code can be generated for many languages. For calls between microservices written in different languages it is a common default.

A browser cannot call it directly — grpc-web translates in between — so it usually stays inside the network. The layer your clients talk to is still REST or GraphQL.

Answer five questions and the guide suggests a starting point. You can change any answer, and the suggestion updates.
Q1Who will call this API?
Q2How many kinds of client will there be, now and soon?
Q3What does the team look like?
Q4Is bandwidth or latency a constraint for your clients?
Q5How many back-end services does one screen read from?
Still 5 to answer. The suggestion appears once all five are done.

The guide in four lines

A public API for outside developers: REST. Many clients and many teams, with one screen assembled from several services: GraphQL, with federation once the graph is large. One team on one TypeScript codebase: tRPC. Calls between internal services in different languages: gRPC. These are starting points rather than rules, and one system often uses more than one of them.

§03

How four teams actually decided

Public decisions, with the reasons the teams gave for them. They did not all decide the same way.

GitHub: added GraphQL, kept REST

GitHub published a GraphQL API in 2016 and described it as the biggest change to its API since it chose JSON over XML. The reason was concrete: every integrator wanted a slightly different set of fields, so the number of custom REST endpoints kept growing. GraphQL let each integrator select fields instead. Ten years later the REST API is still there. GitHub runs both and publishes a page explaining which one to use for what.

One detail is worth noticing. The REST API answers unauthenticated requests, at 60 per hour. The GraphQL API requires a token, and the unauthenticated quota is zero. When a client composes its own query, the server has to know whose budget to charge.

REST · GET /repos/facebook/react (no token needed)
1HTTP/1.1 200 OK
2content-type: application/json
3x-ratelimit-limit: 60
4
5{
6 "id": 10270250,
7 "name": "react",
8 "full_name": "facebook/react",
9 "stargazers_count": 237000,
10 "forks_count": 49000,
11 "open_issues_count": 700,
12 "...": "and many more fields this page will not use"
13}
The page wanted one number and received the whole repository object. That is over-fetching. In exchange, anyone can call it and a CDN can cache the response.
GraphQL · POST /graphql (token required)
1# The same number, one field
2query {
3 repository(owner: "facebook", name: "react") {
4 stargazerCount
5 }
6}
Exactly the requested field comes back. The server asks for a token first, because it cannot predict the cost of a query it has not seen before.

Shopify: moved its Admin API to GraphQL

On 1 October 2024 Shopify marked the REST Admin API as legacy, and from 1 April 2025 new public apps submitted to its app store must use the GraphQL Admin API. The context explains the decision: Shopify's callers are thousands of third-party app developers, and the shapes they need out of products, orders, and inventory vary widely. Rather than maintain two surfaces, Shopify put its investment into the one that lets callers select what they need. That is a strong position, and not the common one — GitHub reached the opposite conclusion with a similar audience.

Netflix: GraphQL as an aggregation layer

Netflix's Studio API combines around 70 internal services into one federated graph, with hundreds of developers contributing to it. Each team owns a subgraph, and a router assembles a query that crosses several of them. Netflix open sourced its Spring Boot framework for this, DGS, in 2021. Notice what did not happen: none of the microservices were replaced. GraphQL sits in front of them so a client makes one request instead of many.

Matt Bessey: six years in, moving away from GraphQL

In 2024 Matt Bessey published Why, after 6 years, I'm over GraphQL, and it was widely discussed. His argument has four parts. Attack surface: a client can compose queries you never planned for, and he measured a 128-byte query that cost about ten seconds of CPU on a public API. Authorization: because any field can be reached through many paths, permission has to be decided per field. Performance: a client can change its query and cause N+1 database queries on a server nobody touched. Complexity: every one of those mitigations is more code to write and to maintain.

He does not claim GraphQL is bad. He says these costs are fixed, and his teams did not have enough clients to spread them over. Operational debugging belongs on the same list: a URL in a log tells you what happened, while POST /graphql with a 200 status does not.

Postman asks several thousand teams every year which API styles they work with. The 2025 answers look like this.

REST93%
Webhooks50%
WebSockets35%
GraphQL33%
“Which API styles does your team work with?” Respondents could choose more than one. Source: Postman, 2025 State of the API, more than 5,700 respondents. 93% of teams work with REST APIs and 33% work with GraphQL, so most teams using GraphQL are using REST as well. Webhooks and WebSockets are event and connection patterns rather than alternatives to either.

How to read these numbers

For a technology open sourced in 2015, a third of teams is significant adoption. It is also not a replacement: REST at 93% is still what nearly everyone works with, and because respondents could choose more than one style, most teams using GraphQL are using REST as well. Together the two numbers describe addition, not succession.

The last misconception to clear up

GraphQL is not a newer version of REST. REST is an architectural style — a set of constraints on how a system is arranged. GraphQL is a query language and a runtime that executes it. They are not points on the same line, and neither one replaces the other. GitHub runs both. Shopify chose GraphQL. Bessey's teams went back to REST. All three decisions were reasonable, because the three situations were different.

§04

Running both

One back end can have more than one surface. This is a normal design, not a compromise.

By now the pattern is visible. A public surface benefits from what REST already has: predictable URLs, HTTP caching, gateways, and tooling every caller owns. Your own clients benefit from selecting fields and from one request per screen. A company with both situations does not have to pick one. It publishes a REST API for outside callers and a GraphQL layer for its own clients, over the same services. That is how GitHub operates.

BFF: where a GraphQL layer usually sits

That inner surface has a name: backend for frontend, or BFF. It is a layer between the general-purpose back end and one group of clients, and its job is to assemble the data those clients need. A GraphQL BFF does not replace your services and does not touch the database directly. It calls the services and returns one response shaped like the query. Netflix's federated graph is this pattern at a very large size.

Most systems use more than one style

File uploads on a REST endpoint or a pre-signed URL. Live updates over a WebSocket or a GraphQL subscription. Calls between internal services over gRPC. Screens that combine several sources through a GraphQL layer. A public API as REST with an OpenAPI document. These are different problems. When someone says a whole system uses only one style, the useful question is whether its problems really are that uniform.

§05

The whole course: twelve chapters, one line each

One sentence per chapter. If a sentence does not feel familiar, open that chapter again.

00 · Client, server, JSONPrologue: what an API isAn API is an agreed way to ask for data and get an answer back. The whole course starts from that agreement.client/serverrequest-responseJSON01 · Methods, status codes, headersHTTP fundamentalsThe method states the intent, the status code states the result, and headers carry the details. REST uses all of it; GraphQL uses HTTP mainly as transport.GET/POSTstatus codesheaders02 · fetch, async/await, DevToolsYour first API callfetch rejects only when the request never completed. A 404 or a 500 still resolves, so you check res.ok yourself.fetchasync/awaitDevTools03 · Six constraints, resourcesThe ideas behind RESTREST is a style, not a protocol: six constraints that produced a web which can be cached, layered, and grown.six constraintsresourcesstateless04 · URLs, CRUD, error formatRESTful API designNouns in the URL, the action in the method, the result in the status code, and errors in a documented format such as RFC 9457.plural nounsCRUD mappingProblem Details05 · Pagination, caching, versioningREST in productionPagination, caching, idempotency keys, and a versioning policy. This is where a production API differs from an example.cursor paginationETag/304Idempotency-Key06 · API keys, JWT, OAuth 2.0, CORSAuthentication and security401 means the server does not know who you are; 403 means it does and still refuses. A JWT is signed, not hidden. OAuth grants access without sharing a password.JWTOAuth + PKCECORS07 · One endpoint, one query languageMeeting GraphQLOver-fetching and under-fetching are the two problems. GraphQL answers them with one endpoint and a query language.one endpointfield selectionquery language08 · SDL, scalars, interfaces, unionsSchema and type systemThe schema is the contract: types, nullability, and deprecations are written down, and introspection lets a client read the current one.SDLNon-Null (!)introspection09 · Query, mutation, subscriptionThe three operationsquery reads, mutation writes, subscription listens. Variables and fragments let you reuse the same query text.three operationsvariablesfragment10 · Resolvers, N+1, DataLoaderServers and performanceEvery field is produced by a resolver. Without batching, one query per item becomes the N+1 problem, and DataLoader is the standard fix.resolverN+1DataLoader · Comparison and decision guideFinale: REST vs GraphQLNeither style replaces the other. The decision comes from your situation, and you can now explain it.choosinghybridtrade-offs
§06

Practice

Three tasks: make three decisions, measure one response, and finish every chapter quiz.

§07

Final quiz

Ten questions covering all twelve chapters. Every wrong option has its own explanation.

QUESTION 01 / 10

A payment service timed out during the night, and the gateway wants to retry the request automatically. Under HTTP semantics, which group of methods is safe to retry, because every method in it is idempotent?

QUESTION 02 / 10

A user left the page open all night and the token expired. In the morning they click "My orders". What should the server answer?

QUESTION 03 / 10

Which of the following is not one of the six REST constraints in Fielding's dissertation?

QUESTION 04 / 10

"Get the paid orders of user 42." Which design holds up best in a code review?

QUESTION 05 / 10

A feed table has hundreds of millions of rows, and users scroll without stopping. What goes wrong with offset pagination such as ?page=50000&per_page=20, and what should replace it?

QUESTION 06 / 10

A colleague put a national ID number into the payload of a JWT, reasoning that "a JWT is signed, so it is safe". What is wrong with that?

QUESTION 07 / 10

The schema says author: User, with no exclamation mark. What does that mean for the client?

QUESTION 08 / 10

posts { author } made 20 posts trigger 21 database queries — the N+1 problem. The standard fix open sourced by Facebook, which collects a batch, runs one query, and caches per request, is called ____ (one English word).

QUESTION 09 / 10

For the same article data, REST's GET /posts/1 can be cached by a CDN, while a GraphQL query in the default setup cannot. What is the underlying reason?

QUESTION 10 / 10

Final judgment. In which of these situations is GraphQL a reasonable default? (Select all that apply)

§08

Where to go next

The course ends here. The part that turns it into a skill happens outside it.

What you can do now

At the start you were asking where the data on a page comes from. Now you can read the status code semantics in an RFC, design a set of URLs a colleague will recognize, explain why a JWT is signed but not hidden, write a resolver that batches its database calls, and give a reason for choosing one API style over another. That is twelve chapters of ground covered.

Route 1 · build it
Build the blog API

Write the blog API with Express or Hono, and store the data in a JSON file. Use what you learned: CRUD mapped onto methods, cursor pagination, the RFC 9457 error format, and an Idempotency-Key on the write that needs one.

Then put a GraphQL layer over the same data, with GraphQL Yoga or Apollo Server. Building both surfaces over one data source is the clearest way to feel the difference.

Route 2 · read the source
Read the specifications

Second-hand summaries go out of date; specifications do not. OpenAPI 3.2 is at spec.openapis.org. The GraphQL tutorial is at graphql.org/learn. The Apollo documentation is at apollographql.com/docs. HTTP semantics are defined in RFC 9110, at rfc-editor.org/rfc/rfc9110.

At your current level these are readable. That is what the course was for.

Route 3 · ship something
Build one small project

Pick a public API: Open-Meteo for a weather panel, PokeAPI for a Pokemon browser, or the Rick and Morty API, which offers both REST and GraphQL. Building the same small project twice, once in each style, will change how you read the table in §01.

A finished project says more than a certificate.

One last thing. This course was not really about REST, and not really about GraphQL. It was about reading a trade-off: what problem does this solve, what does it cost, and does my situation match? New API styles will appear, and someone will announce that an old one is finished. You will be able to check that claim yourself, because you know what to measure. Going from having heard of something to being able to build it, design it, and choose it — that is what learning a subject properly looks like.

Now go and build something.

What to take away from the whole course
  • An API is an agreement about how to ask for data and what comes back. REST, GraphQL, tRPC, and gRPC are different ways of writing that agreement down.
  • HTTP is the shared foundation: methods, status codes, headers, and caching semantics. REST uses all of it. GraphQL uses HTTP mainly as transport, and still runs on top of it.
  • REST is an architectural style: resources behind a uniform interface. Its strengths are that it is cheap to start and that HTTP caching works on GET without extra code. 93% of teams work with REST APIs (Postman, 2025).
  • GraphQL is a query language and a runtime: a schema as the contract, and fields chosen by the client. It is strongest with many clients and with screens assembled from several services. Its costs are mostly fixed — a client cache, depth and complexity limits, batching against N+1, field-level authorization — so they get cheaper per client as the number of clients grows. 33% of teams work with GraphQL, and most of them work with REST as well.
  • The choice comes from the situation: who calls the API, how many kinds of client there are, how the teams are arranged, and whether one screen needs several sources. Neither style replaces the other, and many systems run both. Being able to explain the trade-off is the skill that lasts.