DEV Community

Carlos Oliva Pascual
Carlos Oliva Pascual

Posted on Originally published at stacknotice.com

GraphQL vs REST vs tRPC (2026): Choosing Your API Style

Your API style determines how your team thinks about data, how clients evolve independently of servers, and how much friction exists between backend changes and frontend code.

The Same Resource, Three Ways

A user profile with recent posts:

// REST — two round trips, types maintained manually
const user = await fetch(`/api/users/${id}`).then(r => r.json())
const posts = await fetch(`/api/users/${id}/posts?limit=5`).then(r => r.json())

// GraphQL — one query, client controls the shape, no extra fields
const { data } = await client.query({
  query: gql`query { user(id: $id) { name, bio, recentPosts(limit: 5) { title, slug } } }`,
  variables: { id }
})

// tRPC — one call, fully typed from server return type, no schema
const { data } = trpc.users.getProfile.useQuery({ id, postLimit: 5 })
// data.user and data.posts are typed — TypeScript fails if server changes
Enter fullscreen mode Exit fullscreen mode

Type Safety: The Spectrum

REST GraphQL tRPC
Type source Manual or OpenAPI codegen Schema + codegen Server return types, automatically
Sync required After API change After schema change None — compiler fails immediately
Code generation Optional Required for full safety Never needed

tRPC's key differentiator: change the server return type, TypeScript fails in the client at compile time. No schema to update, no generator to run.

Over-fetching

REST returns fixed shapes regardless of what the client needs. GraphQL solves this at the query level:

# Profile page needs everything
query ProfilePage($id: ID!) {
  user(id: $id) { name, bio, avatarUrl, recentPosts(limit: 5) { title, slug } }
}

# Comment list only needs name + avatar — no other user fields come back
query CommentList($postId: ID!) {
  comments(postId: $postId) {
    content
    author { name, avatarUrl }
  }
}
Enter fullscreen mode Exit fullscreen mode

tRPC controls over-fetching through procedure design — you create separate procedures for different use cases. More explicit, but doesn't allow ad-hoc field selection.

Caching

REST maps to HTTP — CDNs, reverse proxies, and browsers cache GET responses automatically via headers.

GraphQL typically uses POST (even for reads), which HTTP caches ignore by default. Apollo Client and urql provide powerful client-side normalized caches, but CDN-level caching requires extra configuration.

tRPC uses React Query's client-side cache. CDN caching requires wrapping queries as GET requests.

// REST — cache built into every HTTP layer
res.set('Cache-Control', 'public, max-age=60, stale-while-revalidate=300')
// Cloudflare/Fastify caches this without any configuration

// GraphQL — CDN caching needs persisted queries + configuration
// tRPC — React Query cache (client-side, not CDN)
Enter fullscreen mode Exit fullscreen mode

Client Flexibility

REST GraphQL tRPC
Browser (fetch) TS only
Mobile (Swift/Kotlin)
Third-party integrations
curl / Postman Possible

If anyone other than your TypeScript frontend will consume this API, tRPC is not the right primary choice.

Decision Framework

Situation Choose
Public API with external consumers REST
Mobile + web + third-party clients REST or GraphQL
Multiple frontends, different data shapes GraphQL
CDN caching is critical REST
Real-time subscriptions GraphQL
Full-stack TypeScript, one frontend tRPC
Monorepo, tightest type loop tRPC
Non-TypeScript clients exist REST or GraphQL

The practical heuristic: if someone outside your TypeScript frontend will consume the API, tRPC isn't the right primary choice. Multiple clients with different data shapes → GraphQL earns its complexity. One TypeScript frontend where type safety is the priority → tRPC wins on DX.

Hybrid approaches are common: REST for public webhooks and external consumers, tRPC for the internal dashboard, GraphQL for the mobile app — because the right choice depends on the consumer, not just the backend.


Full article at stacknotice.com/blog/graphql-vs-rest-vs-trpc-2026

Top comments (0)