API protocols · Updated 2026-05-24

GraphQL vs tRPC

Pick GraphQL when many clients consume the API and each wants different fields. Pick tRPC when you control both ends in a TypeScript monorepo and want types to flow without a schema layer. If neither shape fits, REST is the default and gRPC is the choice for internal services — see our gRPC vs REST page for that side of the decision.

PLATE — MODERN API SHAPESGRAPHQL · SCHEMA + QUERYtype User {id: ID!name: String!posts: [Post!]!}SDL{ user(id: 42) {name} }client picks2 of 3 fieldsvstRPC · TYPED RPCuser: t.procedure.input(z.number()).query(({input}) => ...)servertypes flowconst u = awaittrpc.user.query(42)→ fully typed, no codegenclientSCHEMA + QUERY · TS-INFERRED RPC
GraphQL
Schema-first query language. Clients ask for exactly the fields they need.
Since
2015
By
Meta / GraphQL Foundation
License
MIT
graphql.org ↗
tRPC
End-to-end typesafe APIs without schemas. Pure TypeScript, no codegen.
Since
2021
By
Alex Johansson + tRPC team
License
MIT
trpc.io ↗

This is the modern API-protocol decision and it has four serious contenders: REST, GraphQL, tRPC, and gRPC. REST is the universal default for any audience that isn't you (covered on a separate page); gRPC is the binary-internal choice (also covered separately). The interesting modern choice for app developers is GraphQL vs tRPC — flexible client-driven queries vs zero-friction TypeScript types. Pick by what shape your client situation has.

Quick takes

If you're…

  • You're building a public API for many client types GraphQL GraphQL lets each client pick its fields. Mobile, web, partners all share one schema.
  • You control both client and server in a TypeScript monorepo tRPC tRPC is unmatched for DX in this case. Types flow from server to client with zero codegen.
  • You need a public API that third-party developers will integrate GraphQL GraphQL has tooling (GraphiQL, Apollo Studio, schema introspection) for external consumers.
  • Your team only speaks TypeScript tRPC tRPC is TS-only by design. If you ever need a non-TS client, you need OpenAPI or codegen.
  • You need real-time subscriptions GraphQL GraphQL has Subscriptions built in. tRPC subscriptions exist but require WebSocket setup.
  • You're looking for HTTP semantics — caching, status codes, REST tooling Either Neither is HTTP-native. If HTTP semantics matter, look at REST first.
  • You need internal service-to-service calls at scale Either Neither is ideal here — gRPC is the canonical answer for this shape.
  • You want to ship product fast in an SSR full-stack app tRPC tRPC + Next.js or SvelteKit removes a whole class of boilerplate.
Decision wizard

A few questions, a verdict.

Q1

Who consumes your API?

Q2

Language mix?

Q3

How much schema overhead can you tolerate?

Q4

Real-time / subscriptions needed?

At a glance

The scorecard.

Dimension
GraphQL
tRPC
Edge
Schema + query language
TS-typed RPC, no schema
depends
SDL schema + codegen
Pure TS inference, no codegen
tRPC
Native: clients pick fields
Predefined shapes per procedure
GraphQL
Polyglot — every major language
TypeScript only by design
GraphQL
GraphiQL + Apollo Studio + many
TanStack Query + minimal tooling
GraphQL
Native subscriptions + graphql-ws
WebSocket / SSE subscriptions
GraphQL
High — schemas, resolvers, DataLoader
Low — just typed functions
tRPC
Decade-old, Meta-grade adoption
5 years old, growing in TS-only shops
GraphQL
In depth

Dimension by dimension.

core

Core abstraction

depends
GraphQL

A query language and a schema. Clients send queries to a single endpoint specifying exactly the fields they need; the server resolves and returns matching shape. One endpoint, many shapes.

tRPC

Typed RPC over HTTP. Server defines procedures (queries + mutations); clients call them like normal TypeScript functions. Types flow via TypeScript inference, no schema needed.

core

Type safety

edge: tRPC
GraphQL

Schema-first typing. SDL (Schema Definition Language) defines types; codegen (graphql-codegen) produces client types. Generated, not inferred — has to be re-run on schema change.

tRPC

TypeScript inference end-to-end. The client gets types from the server's implementation with no codegen step. Changes are reflected immediately. Only works if both ends are TS.

features

Over-fetching prevention

edge: GraphQL
GraphQL

Original design goal. Clients request exactly the fields needed; server returns only those. Massive bandwidth wins on mobile or low-bandwidth contexts.

tRPC

Each procedure returns its predefined shape. To get a subset, the server has to define a new procedure or the client has to call multiple. Less elegant for selective fetching.

core

Language support

edge: GraphQL
GraphQL

Servers and clients in any language. Apollo, urql, Relay for JS; gqlgen for Go; Graphene for Python; Hot Chocolate for .NET; Hasura / PostGraphile for instant Postgres APIs.

tRPC

TypeScript only. Server in TS, client in TS. Trying to consume a tRPC API from Python or Go means writing the request manually — no types, no inference.

ops

Tooling

edge: GraphQL
GraphQL

Mature ecosystem: GraphiQL, Apollo Studio, Hasura Console, GraphQL Yoga, Pothos. Schema explorer is universal. Excellent developer experience for external API consumers.

tRPC

Light by design. TanStack Query integration is the headline feature. No introspection UI, no schema browser — but also no schema to browse.

features

Real-time / subscriptions

edge: GraphQL
GraphQL

Subscriptions are first-class in the spec. graphql-ws transport is standard. WebSocket-based, with reconnection, multiplexing, and auth all defined.

tRPC

Subscriptions exist (since 10.x) using a WebSocket link or Server-Sent Events. Working but requires setup; not as polished as the GraphQL story.

ops

Setup complexity

edge: tRPC
GraphQL

High. Schema definition, resolvers, DataLoader for N+1, query depth limits, complexity analysis. Real ops surface — every Apollo Server outage post-mortem is a teachable moment.

tRPC

Low. `t.procedure.input(z.object({...})).query(({input}) => ...)`. That's the entire API. No schema files, no resolvers, no complexity analysis.

ecosystem

Adoption + community

edge: GraphQL
GraphQL

Used at Meta, Netflix, Airbnb, GitHub, Shopify. Huge installed base. Server-side caches (Apollo, Hasura) are production-grade. Has been in production for a decade.

tRPC

Used by smaller startups, Cal.com, Vercel's own work. Growing fast in the Next.js/SvelteKit ecosystem. About 5 years old. Not used at the biggest tech companies (because they aren't TS-only).

Benchmark

Request overhead, simple "get user" call

Identical Node.js server, 1 KB user payload, 100 concurrent clients via autocannon. GraphQL using Apollo Server 4, tRPC v10 with HTTP batched link, REST + gRPC for context.

Metric
GraphQL
tRPC
Better
p50 latency (GET-shaped)
tRPC has less per-request overhead.
3.8 ms
2.4 ms
tRPC
Bytes on wire (overfetched)
GraphQL wins when client picks 2 of 8 fields; tRPC returns all.
142 B
318 B
GraphQL
Bytes on wire (full object)
When client wants everything, tRPC's smaller protocol overhead wins.
410 B
318 B
tRPC
Bundle size added (client)
Apollo Client vs tRPC + TanStack Query.
38 KB
12 KB
tRPC
CPU per request (server)
Query parsing + resolver dispatch in GraphQL.
220 µs
95 µs
tRPC

Source: tRPC vs GraphQL benchmark (Cal.com engineering) ↗

When to pick neither

A different shape of problem.

  • Public APIs, third-party integrations, HTTP-native everything
  • Internal services, polyglot backend, binary wire format
  • OpenAPI / Swagger
    REST with a real spec, codegen for typed clients in any language
  • JSON-RPC
    RPC mental model over HTTP without GraphQL's schema overhead
  • Hasura / PostGraphile
    You want GraphQL but generated automatically from Postgres
  • Relay
    GraphQL with stronger conventions: cursors, fragments, pagination built in
Situational picks

For specific cases.

Full-stack TypeScript app, single team owns both ends

tRPC

tRPC removes a whole category of API boilerplate. Types flow from server to client with no codegen step. Pair with TanStack Query for caching.

Public API with many third-party consumers

GraphQL

GraphQL's schema is a contract; the introspection story is excellent for external developers. Codegen exists for every language.

Mobile app + web app + partner integrations from one backend

GraphQL

Each client picks exactly the fields it needs. Bandwidth wins on mobile, version drift handled by the schema.

B2B SaaS dashboard, internal team, fast iteration

tRPC

tRPC + Next/SvelteKit + Prisma is the fastest path to shipping. Refactoring server fields shows compile errors in the client immediately.

Internal microservices in a polyglot backend (Go + Python + Java)

gRPC

tRPC is TS-only. GraphQL is overkill for service-to-service. gRPC with .proto contracts is the standard answer — see our gRPC vs REST page.

Public REST API, no need for query flexibility

REST + OpenAPI

REST is still the default for public APIs. Skip GraphQL's complexity tax if your clients don't need its flexibility. Document with OpenAPI.

Sources

Primary material.

Found this useful?