almessadi.
Zur Übersicht

tRPC ist am besten, wenn der gesamte Stack bereits TypeScript spricht_

tRPC entfernt viel API-Duplikation in Full-Stack-TypeScript-Systemen, aber es glänzt nur, wenn die architektonischen Grenzen bereits nahe beieinander liegen.

Veröffentlicht28. Juli 2024
Lesezeit8 min read

tRPC wird gelobt, als ob es jeden API-Stil ersetzt. Das tut es nicht.

Was es tatsächlich sehr gut macht, ist, unnötige Koordinationsarbeit innerhalb eines eng gekoppelten TypeScript-Stacks zu entfernen. Wenn dein Frontend und Backend bereits eng zusammenarbeiten und beide in TypeScript geschrieben sind, kann tRPC viel doppeltes Tippen und Schnittstellenschwankungen vermeiden.

Das ist ein echter Vorteil. Es ist nur nicht universell.

Das Problem, das es löst

In vielen Full-Stack-TypeScript-Apps wird derselbe Vertrag dreimal beschrieben:

  • Eingabever validation
  • Typen der Backend-Handler
  • Typen der Frontend-Konsumenten

Dieses Duplikat ist der Ort, an dem Bugs und veraltete Annahmen einschleichen.

tRPC reduziert viel davon, indem es dem Client ermöglicht, Prozedurtypen direkt aus dem Server-Router abzuleiten:

import { initTRPC } from "@trpc/server";
import { z } from "zod";

const t = initTRPC.create();

export const appRouter = t.router({
  getUser: t.procedure
    .input(z.object({ id: z.string() }))
    .query(async ({ input }) => {
      return db.user.findById(input.id);
    }),
});

export type AppRouter = typeof appRouter;

Auf der Client-Seite musst du keine zweite Version des Vertrags mehr manuell pflegen:

import { createTRPCProxyClient, httpBatchLink } from "@trpc/client";
import type { AppRouter } from "./server";

const trpc = createTRPCProxyClient<AppRouter>({
  links: [httpBatchLink({ url: "/api/trpc" })],
});

const user = await trpc.getUser.query({ id: "123" });

Das ist der Teil, den die Leute mögen: weniger handgeschriebene Nähte.

Warum es sich so gut anfühlt

tRPC ist ausgezeichnet, wenn:

  • die App ein Monorepo oder ein naher Äquivalent ist
  • TypeScript auf beiden Seiten die Sprache ist
  • das Frontend nicht als breit gefächerter öffentlicher API-Konsument gedacht ist

In dieser Welt erhält man einen sehr schnellen Feedback-Zyklus. Benenne ein Feld auf dem Server um, und der Client bricht normalerweise zur Compile-Zeit statt eine Woche später im QA.

Die Kompromisse

tRPC ist schwächer, wenn:

  • mehrere Sprachen die API konsumieren müssen
  • die API öffentlich oder partnerseitig ist
  • das Backend und das Frontend unabhängig voneinander weiterentwickelt werden
  • vertragsbasierte Vereinbarungen auf Protokollebene wichtiger sind als der Komfort der Typableitung

Das ist der Punkt, an dem REST oder gRPC normalerweise mehr Sinn macht. Nicht, weil sie moderner sind, sondern weil sie weniger gekoppelt sind.

Eine bessere Einordnung

tRPC ist nicht "der Tod von REST."

Es ist eine sehr gute Lösung für eine spezifische Kategorie von Produktteams:

  • eine Organisation
  • ein Stack
  • ein gemeinsames Typsystem

Wenn das deine Situation ist, ist es eine starke Option.

Wenn nicht, führt das Erzwingen von tRPC in die Architektur normalerweise zu einem anderen Gesprächsprobleme.

Weiterführende Literatur