Skip to main content
← All Articles Β· Β· 9 min read

GraphQL vs REST vs tRPC 2026: Which API Should You Build?

GraphQL vs REST vs tRPC 2026 comparison: when to use each, performance, type safety, caching, tooling, and decision guide for your next API.

GraphQLRESTtRPCAPI designTypeScriptcomparison2026

Choosing the wrong API architecture is expensive. Migrating from REST to GraphQL mid-project β€” or the reverse β€” costs weeks. This guide cuts through the noise: here’s when REST, GraphQL, and tRPC each win, with side-by-side code comparisons for the same data fetch.

The Contenders

REST (Representational State Transfer) β€” the default choice since the early 2000s, resource-based, HTTP-native.

GraphQL β€” a query language for APIs developed by Meta, single endpoint, client-driven data fetching.

tRPC β€” TypeScript Remote Procedure Call, zero-schema end-to-end type safety for TypeScript-first full-stack apps.


REST: The Universal Standard

Pros

  • Simple mental model β€” everyone understands HTTP verbs and status codes
  • Native caching β€” GET requests cache at browser, CDN, and proxy layers without configuration
  • Universal client support β€” curl, Postman, any HTTP library in any language
  • Stateless β€” each request is self-contained, scales horizontally with ease
  • Great for public APIs β€” no SDK required, any developer can consume it

Cons

  • Over-fetching β€” endpoints return fixed shapes; you often get more data than needed
  • Under-fetching β€” complex UIs require multiple round trips to assemble data
  • No built-in type safety β€” contract drift between server and client is easy to introduce
  • Schema evolution is manual β€” adding fields is safe, removing requires versioning discipline

Example: Fetch a user and their latest 3 orders

// REST: requires 2 round trips
const user = await fetch('/api/users/123').then(r => r.json());
const orders = await fetch('/api/users/123/orders?limit=3').then(r => r.json());

// Assemble on client
const profile = { ...user, recentOrders: orders.data };

GraphQL: Flexible Data Fetching

Pros

  • Precise data fetching β€” clients request exactly the fields they need, nothing more
  • Single endpoint β€” one URL handles all queries and mutations
  • Self-documenting β€” introspection provides a live schema explorer
  • Excellent for complex data graphs β€” deeply nested relational data in one request
  • Strong ecosystem β€” Apollo, Relay, GraphQL Yoga, Pothos, code generation

Cons

  • Caching complexity β€” POST-based queries don’t cache automatically; requires Apollo Client or persisted queries
  • N+1 problem β€” naive resolvers fire one DB query per list item (solved with DataLoader, but requires setup)
  • Overly flexible β€” clients can craft expensive queries; depth limiting and cost analysis needed
  • Overkill for simple APIs β€” heavy setup cost for CRUD-heavy services
  • TypeScript-only safety β€” requires codegen to maintain type sync

Example: Same fetch in GraphQL

// GraphQL: one round trip, exact fields
const { data } = await client.query({
  query: gql`
    query GetUserProfile($userId: ID!) {
      user(id: $userId) {
        id
        name
        email
        avatar
        orders(first: 3, orderBy: CREATED_AT_DESC) {
          edges {
            node {
              id
              total
              status
              createdAt
            }
          }
        }
      }
    }
  `,
  variables: { userId: '123' }
});

tRPC: End-to-End Type Safety Without a Schema

Pros

  • Zero-schema type safety β€” TypeScript types flow from server to client automatically via inference
  • No code generation step β€” types are live, always in sync
  • Excellent DX β€” autocomplete on API calls in your editor
  • Lightweight β€” no runtime schema parsing overhead
  • First-class React Query integration β€” built-in caching, loading states, optimistic updates

Cons

  • TypeScript-only β€” not usable from non-TypeScript clients (no mobile apps, no third-party consumers)
  • Monorepo-friendly, not API-platform-friendly β€” designed for apps that own both client and server
  • No public API use case β€” no documentation format, no SDK generation
  • Tighter coupling β€” server and client must share code or be in the same repo

Example: Same fetch in tRPC

// Server: define procedure
export const appRouter = router({
  userProfile: publicProcedure
    .input(z.object({ userId: z.string() }))
    .query(async ({ input }) => {
      const user = await db.user.findUnique({ where: { id: input.userId } });
      const orders = await db.order.findMany({
        where: { userId: input.userId },
        take: 3,
        orderBy: { createdAt: 'desc' }
      });
      return { ...user, recentOrders: orders };
    }),
});

// Client: fully typed, autocomplete works
const { data } = trpc.userProfile.useQuery({ userId: '123' });
// data.recentOrders[0].status β€” TypeScript knows the shape!

Side-by-Side Comparison

DimensionRESTGraphQLtRPC
Type safetyManual / OpenAPICodegen requiredAutomatic inference
CachingNative HTTPManual setupVia React Query
Public APIExcellentGoodNot suitable
Learning curveLowMediumLow (if you know TypeScript)
Over/under-fetchingCommon issueSolvedNon-issue (server controls shape)
Language supportAnyAnyTypeScript only
SchemaOpenAPISDLNone needed
Best forPublic APIs, microservicesComplex graphs, mobile BFFFull-stack TS monorepos

Decision Guide: Which Should You Build?

Choose REST when:

  • Your API will be consumed by third parties or mobile apps you don’t control
  • You need simple, well-understood HTTP semantics
  • Caching at the CDN layer matters for performance
  • Your team spans multiple languages or backgrounds
  • You’re building a public developer platform (like Stripe, GitHub, Twilio)

Choose GraphQL when:

  • Your data is a complex graph with many relationships (social networks, e-commerce, CMS)
  • You serve multiple clients (web, iOS, Android) with different data needs
  • You want to consolidate multiple REST services behind a single gateway
  • You have a dedicated frontend team that needs data fetching flexibility
  • Real-time subscriptions are a core feature

Choose tRPC when:

  • Your entire stack is TypeScript (Next.js, Node, or similar)
  • You control both the client and server
  • You want maximum developer experience with zero schema boilerplate
  • You’re building an internal tool, SaaS dashboard, or startup MVP
  • You already use React Query and want tighter integration

Migration Considerations

REST β†’ GraphQL

Introduce a GraphQL layer on top of existing REST services. Use schema stitching or federation to aggregate gradually. Don’t attempt a big-bang rewrite.

REST β†’ tRPC

Works best in greenfield projects or when migrating a Next.js app. Extract existing REST handlers into tRPC procedures iteratively.

GraphQL β†’ tRPC

Unusual, but possible if you’ve outgrown the complexity. Replace queries/mutations one-by-one with tRPC procedures in a TypeScript monorepo.


The Honest Take for 2026

REST isn’t dying. GraphQL isn’t winning. tRPC isn’t for everyone.

The best choice depends on who consumes your API and whether you control both ends. Public API with external consumers? REST with a solid OpenAPI spec. Internal full-stack TypeScript app? tRPC eliminates entire categories of bugs. Multi-client product with a complex data model? GraphQL pays off at scale.

Most production systems use a combination: REST for public-facing endpoints, tRPC for internal BFF layers, and sometimes GraphQL as an aggregation gateway over legacy services.

Pick the right tool, not the trendy one.

Free Download

50 CLI Commands That Save Hours Every Week

The exact Git shortcuts, file search tricks, Docker commands, and productivity hacks that senior developers use daily. Compiled from 5+ years of shipping production code.

  • Git mastery (10 cmds)
  • File & search wizardry
  • Docker & DevOps tricks
  • Process & network tools
  • Productivity boosters
  • macOS & Linux install guide

No spam. 1 email/week max. Unsubscribe anytime.

Found this guide useful? Check out our free developer tools.