Fastify and tRPC
A fast, plain HTTP server underneath, and typed procedures on top.
Why tRPC
tRPC gives end-to-end types between the backend and the apps with no schema file and no code generation. You write a function on the server and call it from the client, typed.
The trade-off: tRPC is TypeScript-only. A Python client or a public API would need something else. The plan for that, if it comes up, is trpc-to-openapi for tRPC and Better Auth's OpenAPI plugin for auth.
Why Fastify
Fastify handles everything tRPC doesn't:
- Auth. Better Auth's handler is mounted at
/api/auth/*. - Redirects and callbacks. The desktop sign-in bridge and OAuth callbacks are plain routes.
- Streaming. The chatbot streams replies from
POST /chatbot/streamas newline-delimited JSON. - Hooks. A global
onResponsehook writes the request log.
Where the checks live
Permissions are checked in tRPC middleware, not Fastify hooks. Every procedure builder — protectedProcedure, requirePermission, and the rest — descends from one logged base, so a new procedure gets logging and auth for free.