Что такое tRPC и какую проблему он решает? Как достигается сквозная типобезопасность (end-to-end type safety)?
tRPC — это фреймворк для создания типобезопасных API на TypeScript без генерации схем или кодогенерации. Тип AppRouter экспортируется с сервера и импортируется на клиенте как import type, что даёт полный вывод типов без рантайм-оверхеда.
Что такое tRPC
tRPC (TypeScript Remote Procedure Call) — библиотека для создания API, при которой сервер и клиент написаны на TypeScript и находятся в одном monorepo. Клиент получает полную типизацию серверных процедур — входных параметров, типов ответов и ошибок — без REST-спецификаций, без OpenAPI-схем, без GraphQL SDL и без кодогенерации.
Какую проблему решает
В классическом REST API типы между клиентом и сервером синхронизируются вручную или через кодогенерацию из схем. При изменении серверного эндпоинта клиент узнаёт об этом только в рантайме. tRPC устраняет этот разрыв: если серверная процедура изменила сигнатуру, TypeScript немедленно покажет ошибку в клиентском коде при компиляции.
Как достигается end-to-end type safety
Механизм основан на трёх компонентах:
- Сервер: роутер с типизированными процедурами через Zod-схемы
- Тип AppRouter: экспортируется из серверного файла как TypeScript-тип
- Клиент: параметризуется этим типом через дженерик
// server/router.ts
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
const t = initTRPC.create();
export const appRouter = t.router({
greeting: t.procedure
.input(z.object({ name: z.string() }))
.query(({ input }) => {
return { message: `Hello, ${input.name}!`, timestamp: new Date() };
}),
createUser: t.procedure
.input(z.object({
email: z.string().email(),
role: z.enum(['admin', 'user']),
}))
.mutation(async ({ input }) => {
// Сохраняем в БД...
return { id: crypto.randomUUID(), ...input };
}),
});
// Экспортируем только ТИП — не серверный код!
export type AppRouter = typeof appRouter;
// client/trpc.ts
import { createTRPCReact } from '@trpc/react-query';
import { httpBatchLink } from '@trpc/client';
import type { AppRouter } from '../server/router'; // только тип!
export const trpc = createTRPCReact<AppRouter>();
export const trpcClient = trpc.createClient({
links: [httpBatchLink({ url: '/api/trpc' })],
});
// components/Greeting.tsx
import { trpc } from '../client/trpc';
export function Greeting() {
// TypeScript знает: input = { name: string }, data = { message: string, timestamp: Date }
const { data } = trpc.greeting.useQuery({ name: 'Alice' });
// Ошибка компиляции: 'typo' не существует в AppRouter
// trpc.typo.useQuery();
return <p>{data?.message}</p>;
}
Как работает TypeScript без рантайм-оверхеда
Ключевой момент: import type { AppRouter } — это директива TypeScript, которая полностью удаляется при компиляции. В рантайме клиент не знает ничего о серверной реализации. Типы работают только на уровне TypeScript Language Server и при сборке — никакой рефлексии, никаких дополнительных запросов.
Подводные камни
- tRPC работает только для TypeScript monorepo: клиент и сервер должны иметь доступ к одному типу
AppRouter. Для отдельных сервисов на разных языках нужен другой подход. - Большой
AppRouterс сотнями процедур может замедлить TypeScript Language Server — разбивайте на модули черезmergeRouters. - tRPC не заменяет REST для публичных API: сторонние клиенты (мобильные приложения, внешние сервисы) не могут использовать TypeScript-типы. Для публичного API нужен OpenAPI.
- Zod-валидация выполняется в рантайме на каждый запрос — сложные схемы могут влиять на производительность при высокой нагрузке.
- Ошибки типов в tRPC иногда трудно читать: дженерики глубоко вложены, сообщения об ошибках могут быть длинными. Выделяйте типы в именованные переменные для лучшей диагностики.
- Нет встроенной поддержки версионирования API — при breaking changes все клиенты обновляются одновременно. Для постепенной миграции используйте параллельные роутеры.
Common mistakes
- Смешивать «tRPC» с похожим механизмом без критерия выбора.
- Игнорировать риск: неверно оценить границы применения темы «tRPC» и получить хрупкое решение.
- Показывать только синтаксис и не объяснять поведение в runtime или сборке.
What the interviewer is testing
- Объясняет сквозная типобезопасность между серверным router и клиентским вызовом без отдельной схемы API.
- Показывает на примере, как работает: сервер экспортирует тип
AppRouter, клиент импортирует только тип и получает inference для input, output и errors без code generation. - Называет production-нюанс и граничный случай для темы «tRPC».