Modern APIs: GraphQL, tRPC, and REST in 2026

2/28/2026Forgeora Team
Modern APIs: GraphQL, tRPC, and REST in 2026

Designing APIs for developer productivity and SEO-friendly server-side rendering—what to choose and why.

API design is where backend and frontend teams negotiate. The shape of your API determines how much data travels over the network, how tightly coupled your systems are, and how productive engineers are on both sides. In 2026, three paradigms dominate: REST, GraphQL, and tRPC. Each has a distinct domain where it excels. ## REST: The Universal Language REST (Representational State Transfer) remains the best choice for public APIs and systems with diverse consumers. Its conventions are universally understood, HTTP caching applies naturally to GET endpoints, and OpenAPI/Swagger tooling makes documentation and client generation straightforward. REST's strength is its simplicity and cacheability. A `GET /articles/:id` endpoint is predictable, cache-friendly, and understood by any HTTP client in any language. URL-based resources map intuitively to business entities. REST's weakness is over-fetching and under-fetching. A mobile client that needs only an article's title and author must either receive the full article object or you must maintain multiple endpoint variants for different clients. This "field selection" problem is the primary motivation for GraphQL. **When to choose REST**: - Public APIs with external consumers - Endpoints that benefit from HTTP caching (GET requests for frequently read resources) - Microservice-to-microservice communication where contracts must be stable and versioned - Teams without TypeScript end-to-end ## GraphQL: Flexible Queries, Client-Driven Data GraphQL allows clients to request exactly the fields they need, reducing over-fetching. A React component can declare its data requirements alongside its template, and the GraphQL client (Apollo, urql) batches and deduplicates requests automatically. GraphQL's type system provides a schema that serves as both documentation and contract. Code generation tools (GraphQL Code Generator, Pothos) derive TypeScript types from the schema, keeping client and server in sync. **The N+1 Problem**: GraphQL resolvers can trigger N database queries for N related objects if not handled carefully. DataLoader is the standard solution, batching resolver calls into single database queries. This is a solvable problem but requires explicit attention. **Caching Complexity**: GraphQL runs over POST by default, which CDNs don't cache. Persisted queries (where clients send a query hash rather than the full query text) enable GET requests and CDN caching for known queries. **Subscriptions**: GraphQL subscriptions over WebSockets are the cleanest way to push real-time updates to clients, making GraphQL attractive for live-updating dashboards and collaborative tools. **When to choose GraphQL**: - Product APIs consumed by multiple first-party clients (web, mobile, TV) with different data shape requirements - Applications with complex, interconnected data models - Teams investing in a unified data graph across services - Real-time features that benefit from subscriptions ## tRPC: End-to-End Type Safety for TypeScript Monorepos tRPC is a TypeScript-first RPC framework that generates a fully typed client from your server procedure definitions with zero schema duplication. There's no GraphQL SDL, no OpenAPI YAML—the server code is the schema. ```typescript // server/router.ts export const appRouter = router({ getArticle: publicProcedure .input(z.object({ slug: z.string() })) .query(async ({ input }) => { return await db.article.findUnique({ where: { slug: input.slug } }); }), createComment: protectedProcedure .input(z.object({ articleId: z.string(), body: z.string().min(1) })) .mutation(async ({ input, ctx }) => { return await db.comment.create({ data: { ...input, userId: ctx.user.id } }); }), }); ``` ```typescript // client component const { data: article } = trpc.getArticle.useQuery({ slug: 'my-article' }); // article is fully typed—no codegen step required ``` Changes to the server procedure signature immediately produce TypeScript errors in client code, making API contract violations impossible at compile time. **Limitations**: tRPC works only in TypeScript ecosystems. It's not suitable for public APIs or multi-language backends. It requires shared deployment context (monorepo or npm package sharing) to maintain the type link between server and client. **When to choose tRPC**: - Full-stack TypeScript monorepos (T3 Stack, Next.js with API routes) - Internal APIs consumed exclusively by first-party TypeScript clients - Teams that want maximum type safety with minimum boilerplate - Rapid prototyping where iteration speed matters more than API stability contracts ## API Observability Regardless of style, instrument your APIs consistently: log request/response metadata (not bodies, for privacy), track latency percentiles, measure error rates by endpoint and status code, and correlate traces with a request ID. For GraphQL, track query complexity and resolver timing. For REST, monitor cache hit rates on GET endpoints. ## Schema Evolution Every API must change over time. REST: version URLs (`/v2/articles`) or use header versioning; deprecate old versions with sunset headers. GraphQL: use field deprecation directives and run breaking change detection (GraphQL Inspector) in CI. tRPC: TypeScript compiler catches breaking changes at build time—this is the framework's most significant production benefit.