REST, tRPC and GraphQL solve overlapping but different API problems. The right choice depends on who consumes the API, how clients evolve and what operational complexity the team is prepared to own.

There is no universal winner. Start with the contract and client boundaries, not a framework preference.

Choose REST when the contract should be broadly consumable

REST with resource-oriented HTTP endpoints is a strong default for public APIs, mobile clients, integrations and systems written in multiple languages.

Advantages include:

  • standard HTTP semantics and status codes;
  • straightforward caching for appropriate GET requests;
  • broad tooling and observability support;
  • contracts that can be documented with OpenAPI;
  • loose coupling between client and server implementation languages.

The trade-off is that type sharing is not automatic. Generate clients from an OpenAPI contract or maintain a disciplined schema and compatibility process. Avoid treating generated TypeScript types as runtime validation.

Choose tRPC when the TypeScript boundary is owned by one team

tRPC can provide end-to-end inferred types between a TypeScript server and its TypeScript clients. It is useful when both sides evolve together and the application benefits from a tightly integrated contract.

It is less suitable as a general public API contract for unknown clients. A mobile application written in Swift or Kotlin cannot directly consume TypeScript inference; you need another contract or generated client strategy.

Keep procedures small, validate inputs at runtime and treat authorization as server-side policy. Type safety does not imply access control.

Official documentation: tRPC.

Choose GraphQL when clients need flexible graph-shaped queries

GraphQL is useful when several clients need different projections of a connected data graph, especially when a central schema and resolver layer can replace many bespoke endpoints.

It introduces real costs:

  • schema and resolver design;
  • authorization at field and object boundaries;
  • query complexity and depth limits;
  • caching strategy beyond simple HTTP resource caching;
  • monitoring by operation rather than only endpoint;
  • protection against expensive or abusive queries.

Do not adopt GraphQL solely to avoid writing multiple REST endpoints. Its flexibility is valuable when clients need it and expensive when they do not.

Official documentation: GraphQL.

A practical decision table

Situation Sensible starting point
Public API or third-party integrations REST + OpenAPI
TypeScript web app owns both ends Consider tRPC
Multiple clients need flexible graph queries Consider GraphQL
Native mobile and web clients share a contract REST/OpenAPI or another language-neutral schema
Small service with a few stable operations REST is usually sufficient

Questions to answer before choosing

  1. Who are the clients today, and who might consume the API later?
  2. Do clients and server share a language and release cycle?
  3. Does the data shape vary substantially by client?
  4. What caching behaviour is required?
  5. Can the team operate the schema, resolver or code-generation tooling?
  6. How will authentication, authorization, validation and versioning work?

Do not confuse type safety with correctness

Whichever approach you choose, validate untrusted input at runtime, enforce authorization on the server, define error behaviour and test compatibility. A strongly typed client can still send an unauthorized request or receive an unexpected response from an external service.

Choose the smallest contract that fits the actual consumers. Revisit it when those consumers or requirements change.