← Back to articles

tRPC vs GraphQL vs REST: Which API Style Should You Use? (2026)

The API style you choose shapes your entire development experience. REST is the established standard, GraphQL gives clients flexible queries, and tRPC eliminates the API layer entirely for TypeScript projects. Here's how to decide.

Quick Comparison

FeaturetRPCGraphQLREST
Type safetyEnd-to-end (automatic)With codegenManual (OpenAPI)
SchemaTypeScript typesSDL (Schema Definition Language)OpenAPI/Swagger
OverfetchingN/A (you define returns)Solved (client picks fields)Common problem
Learning curveLow (if you know TS)Medium-highLow
ToolingVS Code IntelliSenseApollo, Relay, AltairPostman, curl, everything
CachingManualNormalized (Apollo)HTTP caching built-in
Public APIsNoYesYes
Language lock-inTypeScript onlyNoNo
File uploadsVia separate routeAwkwardSimple (multipart)

tRPC: End-to-End TypeScript

tRPC lets you call server functions directly from your React components with full type safety. No API definition, no code generation, no fetch calls.

How It Works

// Server: define a procedure
export const appRouter = router({
  getUser: publicProcedure
    .input(z.object({ id: z.string() }))
    .query(async ({ input }) => {
      return db.user.findUnique({ where: { id: input.id } });
    }),
  
  createPost: protectedProcedure
    .input(z.object({ title: z.string(), content: z.string() }))
    .mutation(async ({ input, ctx }) => {
      return db.post.create({ data: { ...input, authorId: ctx.user.id } });
    }),
});

// Client: call it with full type safety
const { data: user } = trpc.getUser.useQuery({ id: "123" });
// TypeScript knows exactly what `user` looks like

Strengths

  • Zero API definition overhead. Change a server function → client types update instantly. No codegen step.
  • Incredible DX. Autocomplete for every procedure, input, and output. Catch errors at compile time, not runtime.
  • Tiny bundle. tRPC's client is ~2KB. No heavy GraphQL client library.
  • Zod validation built-in. Input validation with Zod schemas that also generate types.
  • React Query integration. Built on TanStack React Query — caching, refetching, optimistic updates all included.
  • Subscriptions. WebSocket support for real-time features.

Weaknesses

  • TypeScript only. If your frontend or backend isn't TypeScript, tRPC doesn't work.
  • Monorepo preferred. Works best when client and server share a TypeScript project. Separate repos need type package publishing.
  • Not for public APIs. External consumers can't use your tRPC API without the TypeScript client. No REST endpoints, no GraphQL playground.
  • N+1 queries. No built-in batching/DataLoader pattern like GraphQL. Each procedure call is independent.
  • Scaling complexity. Large procedure routers can become unwieldy. Needs good organization from the start.

Best For

Full-stack TypeScript projects (Next.js, T3 Stack) where frontend and backend are in the same repo or monorepo. Internal APIs that don't need external consumers.

GraphQL: Flexible Client Queries

GraphQL lets clients request exactly the data they need in a single query. The server defines a schema; clients write queries against it.

How It Works

# Schema (server)
type User {
  id: ID!
  name: String!
  posts: [Post!]!
}

type Query {
  user(id: ID!): User
}

# Query (client)
query GetUserWithPosts($id: ID!) {
  user(id: $id) {
    name
    posts {
      title
      createdAt
    }
  }
}

Strengths

  • No overfetching. Client specifies exactly which fields it needs. Mobile gets less data than desktop.
  • Single endpoint. One POST request can fetch data from multiple "resources" — users, posts, and comments in one query.
  • Strong typing. SDL schema is the contract between frontend and backend. Tools like GraphQL Code Generator produce TypeScript types.
  • Introspection. Schema is self-documenting. GraphQL playgrounds let you explore the API interactively.
  • Ecosystem maturity. Apollo, Relay, Urql — mature client libraries with caching, optimistic updates, and pagination.
  • Language-agnostic. Any language can implement a GraphQL server or client.

Weaknesses

  • Complexity. Resolvers, DataLoaders, schema stitching, federation — the GraphQL ecosystem has a steep learning curve.
  • N+1 problem. Without DataLoader, nested queries create N+1 database queries. Solving this adds complexity.
  • No HTTP caching. Everything is POST to one endpoint. CDN caching doesn't work without workarounds (persisted queries).
  • File uploads are awkward. GraphQL doesn't handle multipart uploads natively.
  • Over-engineering risk. For simple CRUD, GraphQL adds significant overhead for minimal benefit.
  • Security surface. Deeply nested queries can cause performance issues (query depth limiting is essential).

Best For

Applications with complex, nested data relationships. Teams with separate frontend and backend teams. APIs consumed by multiple clients (web, mobile, third-party) with different data needs.

REST: The Universal Standard

REST uses HTTP methods (GET, POST, PUT, DELETE) on resource URLs. It's the most widely understood API style and works with every programming language, tool, and platform.

How It Works

GET    /api/users/123           → Get user
POST   /api/users               → Create user  
PUT    /api/users/123           → Update user
DELETE /api/users/123           → Delete user
GET    /api/users/123/posts     → Get user's posts

Strengths

  • Universal. Every language, framework, and tool supports REST. curl, Postman, browsers — it all just works.
  • HTTP caching. GET requests are cacheable by CDNs, browsers, and proxies out of the box.
  • Simple mental model. Resources + HTTP methods. Easy to learn, easy to teach.
  • File uploads. Multipart form data is native and well-supported.
  • Mature ecosystem. OpenAPI/Swagger for documentation, Postman for testing, every HTTP client library.
  • Public API standard. If you're building a public API, REST is the expected format.

Weaknesses

  • Overfetching. Endpoints return fixed data shapes. Mobile clients receive the same payload as desktop.
  • Multiple requests. Getting a user with their posts and comments requires multiple HTTP calls (or custom endpoints that break REST conventions).
  • No built-in type safety. You need OpenAPI/Swagger + codegen for type-safe clients.
  • Versioning is painful. URL versioning (/v1, /v2) or header versioning — both have downsides.
  • Bikeshedding. "Is it PUT or PATCH?" "Should we nest resources?" REST conventions invite endless debate.

Best For

Public APIs, simple CRUD applications, teams with mixed language stacks, and any situation where universal compatibility matters more than developer experience.

Decision Matrix

ScenarioRecommendation
Full-stack TypeScript monorepotRPC
Multiple clients (web + mobile + API)GraphQL or REST
Public/external APIREST
Complex nested data relationshipsGraphQL
Simple CRUD appREST or tRPC
Team with separate FE/BE teamsGraphQL or REST
Maximum type safety, minimum overheadtRPC
Non-TypeScript backendREST or GraphQL
Need HTTP cachingREST
Real-time subscriptions neededGraphQL or tRPC

Can You Combine Them?

Yes, and many teams do:

  • tRPC + REST: Use tRPC for internal frontend-backend communication, expose REST endpoints for external consumers
  • GraphQL + REST: GraphQL gateway federating REST microservices
  • tRPC + GraphQL: Rare, but possible — tRPC for writes, GraphQL for complex reads

Performance

For most applications, performance differences are negligible. The real differences:

  • REST: Fastest for simple requests (HTTP caching, CDN-friendly)
  • GraphQL: Most efficient for complex data needs (fewer round trips)
  • tRPC: Comparable to REST for speed, with near-zero serialization overhead

FAQ

Should I use tRPC or Server Actions in Next.js?

If you're on Next.js App Router, Server Actions handle mutations well. tRPC is better for queries, complex procedures, and when you want React Query's caching. Many projects use both.

Is GraphQL dying?

No, but its growth has slowed. The industry is recognizing that GraphQL's complexity isn't justified for every project. It remains the best choice for complex data needs.

Can I switch from REST to tRPC later?

Yes, incrementally. Add tRPC alongside REST and migrate routes one at a time. Both can coexist in the same application.

What about gRPC?

gRPC is excellent for service-to-service communication (microservices). It's not typically used for browser-to-server communication due to limited browser support. Different use case than the three compared here.

The Verdict

  • tRPC for TypeScript monorepos. Best DX, least overhead, maximum type safety. The default for T3 Stack and similar setups.
  • GraphQL for complex data needs and multi-client APIs. Worth the complexity when you genuinely need flexible queries.
  • REST for public APIs, simple apps, and mixed-language stacks. The safe, universal choice.

In 2026, tRPC is the right choice for most new full-stack TypeScript projects. Use REST for public APIs and GraphQL when your data complexity demands it.

Get AI tool guides in your inbox

Weekly deep-dives on the best AI coding tools, automation platforms, and productivity software.