# REST vs GraphQL vs tRPC > Three API styles with distinct fetch, type, and caching trade-offs. Frontend architecture guidance from fearchitect, written by Abas Turabli and last reviewed 2026-06-21. Source: https://fearchitect.com/topics/rest-vs-graphql-vs-trpc Use it as reference for the task at hand. Before changing code, check this guidance against the codebase: where the code already makes a different, deliberate choice, flag the conflict instead of rewriting it. Library APIs move faster than this guide, so confirm exact signatures in the official docs linked at the end. ## Summary REST, GraphQL, and tRPC are three ways to move data between client and server. REST maps operations to HTTP resources and gets free HTTP caching. GraphQL lets clients shape queries but trades that flexibility for N+1 risk and cache complexity. tRPC skips the schema layer entirely — TypeScript types are the contract — but only works inside a monorepo. ## REST vs GraphQL vs tRPC at a glance | Dimension | REST | GraphQL | tRPC | | --- | --- | --- | --- | | **Fetching** | Fixed shape per endpoint; over/under-fetch is common | Client declares exact fields; one round-trip for nested data | Typed procedure call; shape defined by server return type | | **Typing** | Manual or OpenAPI-generated; drift is possible | Schema + codegen (e.g. GraphQL Code Generator) | Inferred from router — zero codegen, instant type errors | | **Caching** | `Cache-Control: s-maxage` on GET; CDN caches by URL | POST bypasses CDN; persisted queries restore GET caching | TanStack Query handles client cache; no HTTP-level caching | | **Best fit** | Public APIs, CDN-cached reads, non-TS consumers | Diverse clients needing different field sets | Full-stack TypeScript monorepos only | ## Diagram ```mermaid flowchart LR Client -->|"GET /posts/:id\nHTTP caching"| REST["REST\nmulti-endpoint"] Client -->|"POST /graphql\nclient-shaped query"| GQL["GraphQL\nsingle endpoint"] Client -->|"trpc.post.byId\nshared TS types"| TRPC["tRPC\nmonorepo only"] REST --> DB GQL -->|DataLoader| DB TRPC --> DB ``` REST uses multiple endpoints with HTTP caching; GraphQL uses one endpoint with client-shaped queries; tRPC shares TypeScript types directly — no schema layer. ## tRPC router and typed client call Define typed procedures on the server; the exported `AppRouter` type is the sole contract. No schema file, no codegen step. ```ts // server/router.ts import { initTRPC } from "@trpc/server"; import { z } from "zod"; const t = initTRPC.create(); export const appRouter = t.router({ post: t.router({ byId: t.procedure .input(z.object({ id: z.string() })) .query(async ({ input }) => { return db.post.findUnique({ where: { id: input.id } }); }), }), }); export type AppRouter = typeof appRouter; // client/PostPage.tsx import { trpc } from "@/utils/trpc"; export function PostPage({ id }: { id: string }) { // Type of data is inferred from the router — no codegen. const { data } = trpc.post.byId.useQuery({ id }); return

{data?.title}

; } ``` Change the return shape server-side and the client has a type error immediately — no generated files to sync. ## Watch out: GraphQL caching gap Queries sent as HTTP POST bypass CDN caches entirely. Use persisted queries to convert them to GET requests — the client sends a hash, the server expands it — restoring URL-based cache hits. ## Key terms - **Over-fetching**: Receiving more fields than the client needs from a REST endpoint. - **Under-fetching**: Needing multiple REST round-trips because one endpoint lacks required data. - **N+1 problem**: One query for a list plus one query per item — O(N) DB calls instead of two. - **DataLoader**: Batches and caches per-tick GraphQL resolver calls into a single DB query. - **Persisted query**: Stores a query by hash server-side so clients send a GET with just the hash. ## Related topics - [Server State & Data Fetching](https://fearchitect.com/topics/server-state-data-fetching.md): Async, shared, remote-owned data that requires a dedicated cache layer. - [Client State Management](https://fearchitect.com/topics/client-state-management.md): Decide where UI state lives; pick the right tool for the scope. - [Network Performance](https://fearchitect.com/topics/network-performance.md): Hint, prioritize, and pre-navigate to cut request latency. - [Backend for Frontend (BFF)](https://fearchitect.com/topics/backend-for-frontend.md): A per-client server layer that shapes and aggregates APIs for one frontend. - [Caching Strategies](https://fearchitect.com/topics/caching-strategies.md): Layer browser, CDN, and app caches to serve responses without re-fetching. - [Optimistic UI & Mutations](https://fearchitect.com/topics/optimistic-ui-mutations.md): Update the UI before the server replies; roll back on error. ## Further reading - [tRPC docs — quickstart](https://trpc.io/docs/quickstart) - [GraphQL spec](https://spec.graphql.org/October2021/) - [DataLoader — GitHub](https://github.com/graphql/dataloader) - [MDN — HTTP caching](https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching) - [GraphQL persisted queries (Apollo)](https://www.apollographql.com/docs/apollo-server/performance/apq/)