Сервер tRPC собирается как конструктор: initTRPC создаёт заготовку, router собирает процедуры, процедура — это цепочка шагов (проверка ввода, middleware, ваш код), а адаптер подключает всё это к HTTP. В этом уроке разберём каждую деталь на примере сервера заметок — того самого, который в конце модуля превратится в проект.
Контекст: что нужно каждой процедуре
У каждого запроса свои «вещи»: подключение к базе, сведения о том, кто вошёл, заголовки. Всё это собирается в контекст (ctx) — объект, который создаётся заново для каждого запроса и попадает в каждую процедуру. Аналогия: бейдж посетителя в офисе. У каждого посетителя (запроса) он свой, и каждый сотрудник (процедура) смотрит на него, чтобы понять, с кем имеет дело.
// server/context.ts
import { db } from './db';
import { getUserId } from './auth'; // your own function: session, JWT, cookie
export async function createContext(opts: { headers: Headers }) {
return {
db,
userId: await getUserId(opts.headers), // string | null
};
}
export type Context = Awaited<ReturnType<typeof createContext>>;Что внутри getUserId, решаете вы: сессия Auth.js, JWT из заголовка, cookie. Тип контекста TypeScript берёт из возвращаемого значения функции — писать его руками не нужно.
💡 В документации tRPC для Next.js такая функция называется
createTRPCContext. Но функция с тем же именем есть и в@trpc/tanstack-react-query, поэтому в курсе свою мы называемcreateContext— чтобы не путаться.
initTRPC: заготовка сервера
// server/trpc.ts
import { initTRPC } from '@trpc/server';
import type { Context } from './context';
const t = initTRPC.context<Context>().create();
export const router = t.router;
export const publicProcedure = t.procedure;
export const createCallerFactory = t.createCallerFactory;initTRPC.context<Context>().create() создаёт объект t — набор строительных деталей. Мы экспортируем только нужные: router (собирает процедуры), publicProcedure (процедура, которую может вызвать кто угодно) и createCallerFactory (пригодится для вызовов на сервере и тестов). Сам t наружу не отдаём, чтобы все файлы пользовались одним и тем же набором.
Процедуры: query и mutation
Процедура — это конвейер: вызов проходит шаги один за другим, и любой шаг может остановить его ошибкой.
Процедуру собирают цепочкой: начинаем с publicProcedure, добавляем шаги (.input(...), .use(...)) и заканчиваем .query(...) или .mutation(...) — резолвером, то есть вашей функцией.