tRPC vs GraphQL vs REST: Best API Approach (2026)
APIs are the backbone of every app. REST has been the default for 20 years, GraphQL offered flexibility, and tRPC brings end-to-end type safety with zero overhead. Here's how they compare in 2026.
Quick Verdict
| tRPC | GraphQL | REST | |
|---|---|---|---|
| Best for | TypeScript fullstack apps | Multi-client APIs, mobile | Simple CRUD, public APIs |
| Type safety | ⚡ Full end-to-end | 🟡 With codegen | ❌ Manual |
| Performance | Excellent | Good (with care) | Excellent |
| Learning curve | Low (if you know TS) | High | Low |
| Clients | TypeScript only | Any language | Any language |
| Schema | TypeScript types (Zod) | SDL (schema definition) | OpenAPI (optional) |
The Core Problem
REST: Client guesses what the API returns. Types drift. Bugs happen at runtime.
GraphQL: Schema is the contract. But you need codegen to get TypeScript types.
tRPC: Server types ARE the client types. Zero drift. Errors at compile time.
tRPC: Type Safety Without the Ceremony
tRPC removes the API layer entirely — your server functions become directly callable from the client with full TypeScript inference:
// Server — define your procedures
import { initTRPC } from '@trpc/server'
import { z } from 'zod'
const t = initTRPC.create()
export const appRouter = t.router({
getUser: t.procedure
.input(z.object({ id: z.string() }))
.query(async ({ input }) => {
return await db.users.findUnique({ where: { id: input.id } })
}),
createUser: t.procedure
.input(z.object({
name: z.string().min(1),
email: z.string().email(),
}))
.mutation(async ({ input }) => {
return await db.users.create({ data: input })
}),
})
export type AppRouter = typeof appRouter
// Client — fully typed, no codegen
import { createTRPCClient } from '@trpc/client'
import type { AppRouter } from './server'
const trpc = createTRPCClient<AppRouter>({ /* config */ })
const user = await trpc.getUser.query({ id: '123' })
// user is fully typed — hover to see the exact shape
// Typo in field name? TypeScript catches it instantly
Why Teams Choose tRPC
- Zero codegen: Types flow from server to client automatically
- Instant feedback: Change a server response → client shows errors immediately
- Zod validation: Runtime validation and TypeScript types from one source
- React Query integration:
@trpc/react-querywraps TanStack Query perfectly - Subscriptions: WebSocket support built in
tRPC Limitations
- TypeScript only: Both client and server must be TypeScript
- Monorepo preferred: Type sharing works best in a monorepo
- Not for public APIs: External consumers need REST or GraphQL
- Tight coupling: Client and server evolve together
GraphQL: Flexible Querying
GraphQL lets clients request exactly the data they need:
# Schema
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
}
type Query {
user(id: ID!): User
users(limit: Int): [User!]!
}
// Client query — ask for exactly what you need
const GET_USER = gql`
query GetUser($id: ID!) {
user(id: $id) {
name
email
posts {
title
publishedAt
}
}
}
`
Why Teams Choose GraphQL
- Flexible queries: Mobile gets minimal data, web gets everything
- Strong schema: Self-documenting API contract
- Ecosystem: Apollo, Relay, urql — mature tools
- Multi-platform: Any language can query a GraphQL API
- Federation: Combine multiple services into one graph
GraphQL Pain Points
- N+1 queries: Need DataLoader or careful resolver design
- Codegen required: graphql-codegen for TypeScript types
- Over-engineering: Simple CRUD doesn't need GraphQL
- Caching complexity: HTTP caching doesn't work out of the box
- Security: Malicious queries can be expensive (need depth limiting)
REST: The Universal Standard
REST is simple, well-understood, and works everywhere:
// Server
app.get('/users/:id', async (req, res) => {
const user = await db.users.findUnique({
where: { id: req.params.id },
})
res.json(user)
})
// Client
const res = await fetch('/api/users/123')
const user = await res.json()
// No type safety — user is 'any' unless you manually type it
Why REST Still Works
- Universal: Every language, every platform
- HTTP caching: CDN-friendly, cache headers just work
- Simple: No special client libraries needed
- Proven: 20+ years of best practices
- Public APIs: The standard for external consumers
REST Pain Points
- Over-fetching: Get all fields even when you need two
- Under-fetching: Need 3 requests for one screen (user + posts + comments)
- No type contract: Swagger/OpenAPI helps but types drift
- Versioning headaches: /v1/users, /v2/users...
- Custom endpoints: Every new UI need creates a new endpoint
Decision Framework
Choose tRPC When
- Full-stack TypeScript (Next.js, Remix, SvelteKit)
- Monorepo or same-repo client/server
- Team wants maximum type safety
- Internal APIs (not public-facing)
- Rapid iteration speed matters most
Choose GraphQL When
- Multiple clients (web, mobile, partner APIs)
- Deeply nested relational data
- Teams work independently on frontend/backend
- Need a public API with flexible querying
- Already have GraphQL expertise
Choose REST When
- Building a public API
- Simple CRUD operations
- Non-TypeScript clients
- Need maximum HTTP caching
- Team is small and pragmatic
The Hybrid Approach (2026 Best Practice)
Most production apps in 2026 use multiple approaches:
Internal frontend ↔ BFF: tRPC (type safety, speed)
Mobile apps ↔ API: GraphQL (flexible queries)
External/partner ↔ API: REST (universal, cacheable)
Service ↔ Service: gRPC (performance)
FAQ
Can I switch from REST to tRPC?
Yes, and it's straightforward if you're already using TypeScript. tRPC endpoints map 1:1 to REST routes. You can migrate incrementally.
Is GraphQL dying?
No, but its use case has narrowed. In 2026, teams choose GraphQL specifically for multi-client scenarios rather than as a default.
Does tRPC work with Next.js App Router?
Yes. tRPC v11 has first-class support for React Server Components and Next.js App Router with server-side calling.
What about OpenAPI?
OpenAPI is the REST equivalent of a schema. Tools like ts-rest and Hono's OpenAPI integration give you type safety with REST. It's a middle ground between REST and tRPC.
Bottom Line
tRPC if you're full-stack TypeScript — it's the fastest way to build type-safe APIs with zero overhead. GraphQL for multi-platform APIs with complex data needs. REST for public APIs and simple services.
Our pick: tRPC for TypeScript teams — the developer experience is unmatched.