# GraphQL vs REST vs tRPC in 2026: Which API Architecture Should You Choose?

By [Eoin Villarin](https://amvprosox.com/about). Published 2026-02-28, updated 2026-10-28. Canonical: https://amvprosox.com/blog/graphql-vs-rest-vs-trpc-2025

> REST, GraphQL and tRPC are all mature in 2026. Compare type safety, caching, security and team fit, see real code, and use a simple framework to pick the right API style.

## GraphQL vs REST vs tRPC: which API architecture should I choose in 2026?

Choose REST for public APIs, mixed clients and webhooks, GraphQL when many clients need different shapes of the same data, and tRPC when one TypeScript team owns both ends of the API. The GraphQL vs REST vs tRPC decision comes down to who consumes the API and who owns it, and combining them is often the right answer.

**Key takeaways**

- Postman's 2025 State of the API report found 93 percent of respondents used REST versus 33 percent for GraphQL.
- OpenAPI 3.2, released in September 2025, added streaming support, the HTTP QUERY method and hierarchical tags.
- tRPC v11, stable since March 2025, added TanStack Query v5 support plus FormData and binary types like Blob.
- Batch GraphQL resolver lookups with the DataLoader pattern to avoid the N+1 query problem.
- tRPC only makes sense when both ends are TypeScript; native Swift or Kotlin mobile apps cannot use its type inference.

Choosing between **GraphQL vs REST vs tRPC** is one of the earliest architecture decisions on a new project, and one of the hardest to undo. Pick the wrong one and you pay for it in boilerplate, caching headaches or a frontend team blocked on backend changes for months.

All three are mature in 2026. REST gained a new OpenAPI release, GraphQL published its first spec edition in four years, and tRPC v11 has been stable since March 2025. None of them is the "modern" winner. Each fits a different shape of team, client and product.

In this guide you will get a side-by-side comparison, what changed recently for each option, real code, how caching and security differ, where Server Actions and AI agents fit in, and a decision framework you can apply to your next project today.

## GraphQL vs REST vs tRPC: the short answer

If you only read one section, read this table. It reflects how I choose for client projects, from small business sites to multi-client platforms.

![Row of colourful network plugs standing upright, each a different way to connect](https://amvprosox.com/api/img/1793146664942-tmo1ka)
*Three mature options, each built for a different kind of team. Photo: rawpixel, CC0*

| Criteria | REST | GraphQL | tRPC |
| --- | --- | --- | --- |
| Type safety | Via OpenAPI codegen or shared schemas | Via schema and codegen | End to end, no codegen |
| HTTP and CDN caching | Native for GET | Needs persisted queries or client cache | Possible for queries, rarely used |
| Client languages | Any | Any | TypeScript only |
| Over-fetching control | Per endpoint | Per query, client decides | Per procedure |
| Learning curve | Low | Medium to high | Low for TypeScript teams |
| Best for | Public APIs, mixed clients, webhooks | Many clients with different data needs | Internal APIs in TypeScript monorepos |

Short version: **REST for anything public or polyglot, GraphQL when many clients need different shapes of the same data, tRPC when one TypeScript team owns both ends.**

## Why is REST still the default in 2026?

REST remains the most widely used API style by a large margin. In Postman's [2025 State of the API report](https://www.postman.com/state-of-api/2025/), which surveyed over 5,700 developers, architects and executives, 93 percent of respondents used REST, compared with 33 percent for GraphQL. Every HTTP client, from browsers and mobile apps to curl and no-code tools, speaks it natively.

The tooling also keeps improving. [OpenAPI 3.2](https://spec.openapis.org/oas/v3.2), released in September 2025, added hierarchical tags, support for streaming and sequential data, the HTTP `QUERY` method and the OAuth 2.0 Device Authorization Flow. A good OpenAPI document gives you generated clients, documentation and contract tests in almost any language.

Pair REST with runtime validation and you get most of the type safety people move to other styles for:

```
// app/api/products/[id]/route.ts
import * as z from 'zod'

const Params = z.object({ id: z.string().regex(/^\d+$/) })

export async function GET(_req: Request, ctx: RouteContext) {
  const { id } = Params.parse(await ctx.params)
  const product = await db.product.findUnique({ where: { id } })
  if (!product) return Response.json({ error: 'Not found' }, { status: 404 })

  return Response.json(product, {
    headers: { 'Cache-Control': 'public, s-maxage=300, stale-while-revalidate=600' },
  })
}
```

That one `Cache-Control` header lets a CDN serve the response for five minutes without touching your server. I go deeper on schema-first validation in my guide to [type-safe APIs with TypeScript and Zod](https://amvprosox.com/blog/typescript-zod-type-safe-api-validation).

## When does GraphQL earn its complexity?

GraphQL shines when several clients, such as a web app, a mobile app and partner integrations, need different slices of the same data. Instead of building a new endpoint per screen, you publish one schema and each client asks for exactly the fields it needs. The schema becomes the contract between frontend and backend teams.

Shopify is a good real-world example. Its Storefront API is GraphQL, which is why headless storefronts can fetch a product, its variants and its market price in one request, as I show in my [Shopify Hydrogen guide](https://amvprosox.com/blog/shopify-hydrogen-headless-commerce-2025).

### What the September 2025 spec added

The [September 2025 edition of the GraphQL specification](https://graphql.org/blog/2025-09-08-september-edition/) was the first new edition since October 2021. It standardized schema coordinates for referencing schema elements, descriptions on executable documents, and OneOf input objects for mutually exclusive inputs:

```
input ProductLookup @oneOf {
  id: ID
  handle: String
}

type Query {
  product(by: ProductLookup!): Product
}
```

### The costs you must plan for

Resolvers that fetch related data one item at a time create the N+1 problem: one query for 50 products, then 50 more for their reviews. Batch those lookups with the DataLoader pattern:

```
import DataLoader from 'dataloader'

const reviewsByProduct = new DataLoader(async (productIds: readonly string[]) => {
  const rows = await db.review.findMany({ where: { productId: { in: [...productIds] } } })
  return productIds.map((id) => rows.filter((r) => r.productId === id))
})
```

You also need query depth and complexity limits, a strategy for caching (usually persisted queries plus a normalized client cache like Apollo or urql), and someone who owns the schema. For a single small team, that overhead often outweighs the flexibility.

## Is tRPC the best choice for full-stack TypeScript?

tRPC removes the API contract as a separate artifact. You write a procedure on the server, export its type, and the client gets autocomplete and compile errors from it directly. There is no schema language, no OpenAPI file and no code generation step.

![Single network cable with matching connectors at both ends, like types shared end to end](https://amvprosox.com/api/img/1793146666053-9rhwlx)
*One language, one repository, types shared end to end. Photo: rawpixel, CC0*

```
// server/router.ts
import { initTRPC } from '@trpc/server'
import * as z from 'zod'

const t = initTRPC.create()

export const appRouter = t.router({
  productById: t.procedure
    .input(z.object({ id: z.string() }))
    .query(({ input }) => db.product.findUnique({ where: { id: input.id } })),
})

export type AppRouter = typeof appRouter

// client component
const trpc = useTRPC()
const { data: product } = useQuery(trpc.productById.queryOptions({ id }))
```

The [tRPC v11 release](https://trpc.io/blog/announcing-trpc-11) added support for TanStack Query v5 with a new TanStack React Query integration (the `queryOptions` pattern above), plus FormData and binary types such as Blob, File and Uint8Array. It is largely backward compatible with v10.

The limitation is fundamental: tRPC only makes sense when both ends are TypeScript and deploy together. A native mobile app in Swift or Kotlin, a partner integration or a public developer API cannot consume it comfortably. Many teams run tRPC internally and expose a small REST API for everything else.

## How do caching, performance and security compare?

### Caching

REST wins on HTTP caching because each resource has its own URL and GET responses can be cached by browsers and CDNs with standard headers. GraphQL usually sends POST requests to one endpoint, so CDN caching requires persisted queries sent as GET. tRPC sends queries as GET, but request batching and per-user data mean most teams rely on TanStack Query's client cache instead.

### Performance

Raw transport speed is rarely the bottleneck. What matters is round trips and payload size. GraphQL reduces round trips for complex screens, REST can match it with well-designed composite endpoints, and tRPC batches multiple calls into one request. Slow APIs are almost always slow because of database queries, not the API style.

### Security

- **REST:** authorize per endpoint and per resource, validate input, and rate limit by route.

- **GraphQL:** authorize at the field or resolver level, limit depth and complexity, and consider allowlisting persisted queries in production.

- **tRPC:** use middleware for authentication and validate every input with a schema; types do not protect you from malicious requests.

> Type safety is a developer experience feature, not a security feature. Every API style still needs runtime validation and authorization on the server.

## Where do Server Actions and AI agents fit in?

If you build with Next.js, Server Actions cover many internal mutations without any API layer. A form can call a server function directly, which is often simpler than adding a tRPC router or REST endpoint for one button. They are not a public API, though, and they do not replace a proper contract for other clients.

![Person seen from behind drawing an architecture diagram on a whiteboard](https://amvprosox.com/api/img/1793146667228-i6ihzy)
*Decide based on clients, team and ownership, not trends. Photo: rawpixel, CC0*

AI agents are a newer client to plan for. Agents and AI assistants work best with clearly described, stable operations, which is a natural fit for REST with an OpenAPI document or for tools exposed through the Model Context Protocol. I cover that side in my guide to [making your website agent-ready](https://amvprosox.com/blog/agent-ready-website-structured-data-llms-txt-mcp).

## A decision framework you can use today

- **Will anyone outside your team consume the API?** If yes, start with REST and OpenAPI.

- **Are all clients TypeScript, in one repository, deployed together?** If yes, tRPC gives the fastest development loop.

- **Do three or more clients need different data from the same domain?** If yes, and you can staff schema ownership, GraphQL pays off.

- **Is the main need CDN-cached public content?** REST is the simplest path.

- **Is it a single Next.js app with forms?** Server Actions may be enough on their own.

Mixing is normal. A common stack I build is tRPC or Server Actions inside the app, a small REST API for webhooks and partners, and GraphQL only where a third-party platform like Shopify already provides it. For a full example of that kind of stack, see my [Next.js and Supabase full-stack guide](https://amvprosox.com/blog/full-stack-app-nextjs-supabase-2025).

## Frequently asked questions

### Is GraphQL faster than REST?

Not inherently. GraphQL can reduce the number of requests for complex screens because one query fetches nested data, but each query can also be heavier to resolve. REST is easier to cache at the CDN level. In practice, database design and caching strategy decide speed far more than the API style you choose.

### Can I use tRPC with a mobile app?

Only if the mobile app is written in TypeScript, for example with React Native or Expo in the same monorepo. Native Swift or Kotlin apps cannot use tRPC's type inference, so you would lose its main benefit. For mixed clients, expose REST or GraphQL endpoints alongside your internal tRPC router.

### Is REST outdated in 2026?

No. REST is still used by the large majority of developers, and OpenAPI 3.2 added streaming support, the QUERY method and better tag organization in 2025. It remains the best default for public APIs, webhooks and integrations with mixed clients, and it is the easiest style for AI agents and tools to consume.

### Can I migrate from tRPC to REST or GraphQL later?

Yes. Because tRPC procedures are plain functions with validated inputs, you can wrap the same business logic in REST routes or GraphQL resolvers when you need external clients. Keep business logic in separate service functions rather than inside procedures, and a later migration becomes a thin adapter layer.

## Choosing your API architecture

The GraphQL vs REST vs tRPC decision comes down to who consumes your API and who owns both ends of it. Use REST for public and mixed clients, GraphQL for many clients with different data needs and a team to own the schema, and tRPC for internal TypeScript-only stacks. Combining them is often the right answer.

If you are planning a new app or untangling an existing API, [contact me through my contact form](https://amvprosox.com/#contact-form) and tell me about your clients and team. You can also browse my [development case studies](https://amvprosox.com/case-studies) to see the platforms I have built.
