intermediate

tRPC API boundaries

Используйте tRPC для TypeScript monorepo productivity, учитывая runtime validation, auth, deployment и public-contract limits.

tRPC разделяет TypeScript types между client и server через routers и procedures — отличная продуктивность в **monorepo** без ручной синхронизации OpenAPI.

					const appRouter = router({
  order: router({
    byId: publicProcedure
      .input(z.object({ id: z.string().uuid() }))
      .query(({ input, ctx }) => ctx.orders.getById(input.id)),
    create: protectedProcedure
      .input(createOrderSchema)
      .mutation(({ input, ctx }) => ctx.orders.create(input)),
  }),
});
export type AppRouter = typeof appRouter;
				

Границы, которые важно соблюдать:

  • **Runtime validation** (Zod) даже при static types — внешний input недоверенный.
  • **Auth middleware** на procedures (`protectedProcedure`), не ad-hoc checks в handlers.
  • **Public vs internal**: tRPC силён для first-party apps; third-party public API обычно нужен OpenAPI/REST для language-agnostic контрактов.
  • **Deployment**: отдельные serverless functions могут требовать HTTP batching config; WebSockets для subscriptions опциональны и зависят от infra.

На интервью: когда tRPC уместен (TS full-stack команда), как дать стабильный external API рядом с tRPC, validation на границе.

Типовые ошибки: доверие compile-time types в runtime, утечка `AppRouter` types недоверенным consumers, god routers, нет дисциплины формы ошибок.

Компромисс: end-to-end TS speed против openness экосистемы — документируйте escape hatches для non-TS clients.

Чеклист:

  • Zod (или аналог) на input каждой procedure.
  • Auth как composable middleware.
  • Отдельный public BFF от internal routers при необходимости.
  • Согласованный error mapping для клиента.