~/comparisons/rest-api-vs-graphql-in-2026-which-one-to-pick-for-a-new-product
§ POST · MAY 10, 2026 v1.0

REST API vs GraphQL in 2026: which one to pick for a new product

REST vs GraphQL in 2026: the 5 questions that decide it (clients, caching, team, scope, latency), real downsides of each, and where tRPC fits between them.
Ryan CallowayStaff contributor
  9 min read

By Ryan Calloway. Updated May 2026.

“GraphQL is a trap” was the 2023 Hacker News thread that ran 500+ comments and never broke its top spot for the day. Three years later the data agrees more than it disagrees: the Stack Overflow 2025 Developer Survey still shows REST and JSON as the dominant API surface for professional developers, with GraphQL stuck in the low-teens single-digit range it has occupied since 2022. The Postman 2024 State of the API report puts REST at 82% of APIs developers consume; GraphQL at 21%, gRPC at 12%, with most teams using more than one. The recurring r/webdev “GraphQL vs REST” thread reaches the same divide: REST for service-to-service and public APIs, GraphQL when the frontend really needs per-field shape control. This is the comparison from one product spec, the five questions that decide it for a new product, and where tRPC fits between them.

Verdict at a glance

  • Best for: REST + OpenAPI 3.1 for new public APIs, service-to-service, and CRUD-heavy backends — cacheable on the CDN, predictable, multi-language clients trivial.
  • Not best for: GraphQL on a single-frontend CRUD app or anything where per-edge CDN caching is a serious cost line.
  • Watch out for: GraphQL N+1 (DataLoader is mandatory, not optional); REST over-fetching on deep object graphs; tRPC lock-in to a TypeScript-only stack.
  • Use GraphQL (Apollo Server 4.x / Yoga 5.x / Hasura) for: multi-frontend products with diverse data shape needs, or as a BFF aggregating multiple internal services.

Quick answer

Default to REST for a new product in 2026. Use GraphQL when the API is consumed by multiple frontends with different data-shape needs, or as a BFF on top of internal REST services. Use tRPC when both ends are TypeScript and you control the whole monorepo. Most production APIs in 2026 are still REST — reach for GraphQL when field-level fetching is a real product need, not a resume bullet. The five questions below tell you which bucket your project is in.

The short comparison

Dimension REST + OpenAPI 3.1 GraphQL (Apollo 4.x / Yoga 5.x / urql / Hasura) tRPC v11
Spec HTTP + JSON, conventional Single endpoint, typed schema, GraphQL October 2021 spec TypeScript end-to-end, no schema language
Caching HTTP (CDN, browser, ETag) — free Application-level (Apollo Client, urql); GET persisted queries on CDN with effort App-level via React Query / TanStack Query
Versioning URL or header (/v2/users) Schema evolution + @deprecated Re-export router; major version per breaking change
Tooling OpenAPI 3.1, Bruno, Postman, codegen for every language GraphQL Codegen, Apollo Studio, Hasura console Type inference; no codegen needed in the same repo
Authn / authz Per-route, easy to audit Per-resolver / per-field; harder Procedure-level, easy in TS
N+1 risk Manageable with denormalized routes Real; DataLoader mandatory Same as your underlying ORM — you control the SQL
Public API Standard, predictable Less common, harder for third-party adoption Internal monorepo only
Postman 2024 adoption ~82% ~21% n/p in survey; growing in TS monorepos

5 questions that decide it for a new product

1. How many distinct clients consume the API, and how different are their data needs?

This is the question GraphQL was actually built to answer. If you have one frontend (or one mobile app and one web app showing nearly the same data), REST plus a couple of denormalized “compound” endpoints is cheaper. If you have a marketing website, an admin dashboard, a customer mobile app, and a partner integration all pulling subsets of the same graph, the per-frontend backend churn is real and GraphQL pays for itself. Facebook published the original GraphQL motivation post in 2015 explicitly because of this. If your client topology looks like Facebook’s, GraphQL is the answer. If it looks like a typical SaaS in 2026 (web app + iOS + Android), the case is weaker than the marketing suggests.

2. How much does HTTP-level caching matter to your cost line?

REST runs over HTTP, which has 30 years of cache infrastructure. CDNs cache responses by URL. Browsers respect Cache-Control. Conditional If-None-Match with ETag avoids a body round-trip on unchanged data. None of this requires application code. The MDN HTTP caching guide covers the headers in detail.

GraphQL traffic is almost always POST requests with a body, which CDNs cannot cache by default. You bolt on Apollo Client or urql normalised cache on the frontend, plus a Redis layer on the backend, plus persisted queries with GET on Cloudflare or Fastly to get edge caching. It works. It is more code and more configuration. For a public API with hot endpoints, REST gives you 80% of the win for free.

3. What’s your team’s surface-area for resolver discipline?

The GraphQL example below looks like one query but runs as N+1 if you implement resolvers naively:

// Bad: 1 user query, 5 order queries, 1 item query per order = 11 round trips
const userResolver = (id) => db.users.findById(id);
const ordersResolver = (user) => db.orders.where({ userId: user.id }).limit(5);
const itemsResolver = (order) => db.items.where({ orderId: order.id });

The standard fix is DataLoader: batch and dedupe within a request.

const itemsByOrderLoader = new DataLoader(async (orderIds) => {
  const items = await db.items.whereIn("orderId", orderIds);
  return orderIds.map(id => items.filter(i => i.orderId === id));
});

Every GraphQL backend in the wild ends up adopting DataLoader (or graphql-resolve-batch, or framework-specific equivalents like Hasura’s relationship-based query planner). The recurring “N+1 problem in GraphQL” r/graphql thread shows the discovery and the wiring is a real time cost on every team’s first GraphQL launch. Budget a week to wire it through every relation in your schema, plus ongoing review discipline. REST teams hit the same N+1 problem internally but on their terms; the API surface does not expose it.

4. What’s the public/private split, and the third-party integration story?

If anyone outside your team will integrate, REST is the standard. Stripe, Twilio, Slack, GitHub: all REST-first, GraphQL second (if at all). Even GitHub’s public GraphQL API is still maintained alongside their REST API; partner integrations almost universally choose REST because every language and tool has a battle-tested HTTP client. For internal services, GraphQL is fine; for a developer platform expecting third-party adoption, REST plus an OpenAPI 3.1 spec gets you SDK generation in 30+ languages out of the box.

5. What’s your latency sensitivity per user flow?

Per request, GraphQL is usually a bit slower than equivalent REST because of resolver dispatch and parsing overhead. Per user flow, GraphQL is often faster because fewer round trips. A mobile checkout that needs user, cart, addresses, payment methods, and shipping options is one GraphQL query versus five REST calls; on a 4G connection with 200 ms RTT, that is a real one-second difference. On a fast desktop connection talking to a same-region API, the gap is single-digit milliseconds. Measure on your real flows; folklore is wrong half the time.

The same endpoint, both ways

Get a user with their last 5 orders, including order line-items.

REST + OpenAPI 3.1 (3 round trips, 2 over-fetched)

GET /users/42                                  -> { id, name, email, address, prefs, ... }
GET /users/42/orders?limit=5                   -> [{ id, total, ... }, ...]
GET /orders/{id}/items                         -> [{ sku, qty, price }, ...]   x5

Seven HTTP requests. Each returns the full resource, including fields the UI does not need. The fix is a compound endpoint: GET /users/42?include=orders.items&orders.limit=5. You ship one denormalised response shape per UI need. That works for one frontend; it scales linearly with frontends.

GraphQL (1 round trip, exactly the fields needed)

query {
  user(id: 42) {
    name
    orders(last: 5) {
      total
      items { sku qty }
    }
  }
}

One request. The server resolves user, then orders, then items in one round trip. The payload is exactly what the UI asked for. Apollo Server 4.x and GraphQL Yoga 5.x are the two production-stable JavaScript servers; Hasura generates the schema directly from your Postgres tables. urql is the lightest-weight client if Apollo’s bundle size bothers you.

tRPC v11 (TypeScript end-to-end, monorepo only)

// server/router.ts
export const appRouter = router({
  user: publicProcedure
    .input(z.object({ id: z.number() }))
    .query(({ input, ctx }) => ctx.db.users.findById(input.id)),
});

// client/page.tsx
const { data } = trpc.user.useQuery({ id: 42 });
//      ^^ fully typed, no codegen, no schema file

Inside a monorepo where both ends are TypeScript, tRPC removes the schema-language step entirely. Your router is the schema; type inference flows from server to client. The trade-off is real: tRPC is a TypeScript-only solution, and it is internal-only by design. A Swift mobile client cannot consume it. For a typed Next.js + Node monorepo with no third-party API exposure, tRPC is often the right answer over both REST and GraphQL.

Versioning: REST is messier, GraphQL is sneaky

REST versioning is unsubtle. /v1/users stays forever, /v2/users launches alongside, you deprecate v1 on a schedule. Heavy-handed and predictable. GraphQL has no version. You add fields, you mark old ones with the @deprecated directive, clients that ignore the deprecation keep working until you remove the field. This sounds clean, and is, until a client team’s app crashes because you removed a field they still depended on. Field-usage analytics, deprecation windows, and explicit comms to clients are how mature GraphQL teams stay sane. Less mature teams rediscover the discipline after their first incident.

Authn and authz: REST per-route is easier

REST authorization is middleware on the route, returns 401 or 403 cleanly, easy to audit which endpoints require what role. GraphQL authorization is per-field (or per-type) because a single query can read 30 fields across 5 types. You either centralise in resolver wrappers, use directives like @auth(role: "admin"), or push it into the data layer. All three approaches work; reasoning about who can read what is harder than with REST. For a public API or a high-stakes audit boundary (HIPAA, PCI), REST’s per-endpoint mental model wins on auditability.

The hybrid pattern most teams converge on

REST for service-to-service and public APIs. GraphQL as a BFF in front of those REST services for the web app. The internal services stay REST (cacheable, simple, well-tooled), the BFF gives the frontend per-query shape control. Netflix, Airbnb, and Shopify have all written about this hybrid; the engineering blog posts use different vocabulary but describe the same architecture. If you are starting a new product in 2026 and you genuinely need per-field control, ship REST first and add a BFF when the second frontend lands. Do not start with GraphQL by default and discover the operational cost six months in.

FAQ

Is GraphQL replacing REST in 2026?

No. Adoption flattened in 2023 and stayed there through the 2025 surveys. REST remains the default; GraphQL is one tool in the toolbox for the data-shape problem. Most production APIs in 2026 are still REST.

What about gRPC, tRPC, or Connect?

gRPC for service-to-service binary RPC, especially in polyglot environments where Protocol Buffers as a contract format works. tRPC for end-to-end type safety in TypeScript monorepos. Connect (Buf) for cross-runtime gRPC with HTTP fallback so browsers can call gRPC services without a sidecar. All three are valid for specific stacks; they are not REST-vs-GraphQL alternatives, they are different points on the same axis.

Can a GraphQL endpoint be cached on a CDN?

With effort. GET-based persisted queries (Apollo and Relay support them) are cacheable by URL on Cloudflare, Fastly, and Akamai. Response-level caching at the application layer (Apollo Server’s responseCachePlugin, Hasura’s query cache) is common. CDN-level caching is achievable, not free.

Is REST faster than GraphQL?

Per request, REST is usually a bit faster because there is no resolver dispatch or query parsing. Per user flow, GraphQL is often faster because fewer round trips. On a fast network, the difference is invisible; on mobile networks with 200+ ms RTT, the round-trip count dominates. Measure your real flows.

How do I document a REST API in 2026?

OpenAPI 3.1. Generate the spec from your code (FastAPI, Spring Boot, NestJS do this natively) or write the spec first and generate clients. Scalar and Stoplight render OpenAPI into developer portals; Bruno is the offline-first Postman alternative most teams settled on after the 2024 Postman cloud-only transition.

What about Hasura specifically?

Hasura is GraphQL generated automatically from your Postgres or SQL Server schema. It collapses the resolver-writing step entirely and handles N+1 internally through its query planner. For internal admin tools and dashboards on top of a relational database, it ships the API in an afternoon. For complex business logic or non-database aggregation, you still write resolvers in your own server.

Sources and further reading

If you picked REST, the framework decision is next; the FastAPI vs Flask vs Django REST guide covers the Python side. If your REST API needs to handle CORS, the CORS error fix guide is the operational follow-up. For deployment, the Docker Compose tutorial walks through Node.js + Postgres + Redis end to end.

esc