tRPC for Young Coders
Introduction to tRPC
What is tRPC?
Imagine your app's frontend (the part users see) needs to ask the backend (the server) for some data. Traditionally, this involves carefully crafting an API, which acts like a menu at a restaurant. The frontend orders from the menu, and the backend prepares the dish.
tRPC, which stands for TypeScript Remote Procedure Call, simplifies this process. Instead of a menu, it feels more like the frontend is in the same kitchen as the backend. The frontend can just call a function on the server as if it were a local function, and TypeScript ensures the request and the response are exactly what both sides expect.
With tRPC, you don't build an API; you just write functions. Your TypeScript types become the single source of truth that connects your client and server.
This tight integration with TypeScript is tRPC's superpower. It doesn't require a separate schema file or code generation steps. The types are inferred directly from your server-side TypeScript code, creating a seamless, typesafe connection from end to end.
Beyond REST and GraphQL
To understand why tRPC is different, let's look at the common alternatives.
REST APIs are the classic approach. You define endpoints, like /api/users/1, and the client sends a request to that URL. The problem is that the client and server have no shared understanding of the data structure. If the server changes the user object, the client won't know until something breaks at runtime.
GraphQL improved this by introducing a schema. This schema acts as a contract between the client and server, defining all the possible queries and data shapes. This is great for type safety, but it adds complexity. You have to maintain the schema, and often use tools to generate TypeScript types from it.
tRPC takes a more direct route. Since your backend and frontend are both written in TypeScript, it leverages the language itself to create the contract. There's no extra step and no code generation.
| Approach | Type Safety | How It Works |
|---|---|---|
| REST | Manual | Client calls specific URL endpoints. Data shapes are based on documentation or convention. |
| GraphQL | Schema-based | Client sends a query matching a predefined schema. Types can be generated from the schema. |
| tRPC | Inferred | Client calls server functions directly. Types are automatically inferred from the TypeScript code. |
The Joy of Type Safety
End-to-end type safety isn't just a technical luxury; it dramatically improves the developer experience. Think of it as having guardrails that prevent you from making common mistakes.
When your client code calls a server function, your code editor knows exactly what that function is called, what arguments it needs, and what the data it returns will look like. You get full autocompletion, right in your editor. If you try to pass the wrong type of data, TypeScript will immediately alert you.
This connection also makes refactoring a breeze. If you rename a function or change a data type on the server, TypeScript will instantly show you all the places in your client code that need to be updated. This catches bugs before they ever make it to your users, saving you from tedious debugging sessions.
The main benefit is confidence. You can change your API on the backend and immediately see the impact on the frontend, without ever leaving your editor.
Finally, tRPC is framework-agnostic. While it's popular in the Next.js and React ecosystems, you can use it with almost any frontend framework (like Vue or Svelte) and any Node.js server framework (like Express or Fastify). This flexibility allows you to bring end-to-end type safety to a wide variety of projects.
What is the primary benefit of tRPC's end-to-end type safety?
How does tRPC's approach to API contracts differ from GraphQL?
That's a quick look at what makes tRPC a compelling choice for building modern web applications. It simplifies API development by making type safety the default, not an afterthought.
