tRPC Adapter
When your frontend is TypeScript, tRPC lets you call procedures with full type inference — autocompletion, type errors at compile time, and no manual type definitions. Velox TS builds the tRPC router automatically from your procedure collections; this page covers how to configure and export it.
Quick Start
Section titled “Quick Start”Use the rpc() helper to create a typed tRPC router:
import { rpc } from '@veloxts/velox';import { userProcedures } from './procedures/users';import { postProcedures } from './procedures/posts';
// IMPORTANT: Use `as const` for full type inferenceexport const collections = [userProcedures, postProcedures] as const;
// Create typed tRPC routerconst { router } = rpc(collections, { prefix: '/trpc' });
export { router };export type AppRouter = typeof router;Register with your app:
import { veloxApp, registerRpc, rest } from '@veloxts/velox';import { collections } from './router.js';
const app = await veloxApp({ port: 3030 });
// Register tRPC at /trpcawait registerRpc(app, collections, { prefix: '/trpc' });
// Optionally register REST at /apiapp.routes(rest([...collections], { prefix: '/api' }));
await app.start();API Reference
Section titled “API Reference”rpc(collections, options)
Section titled “rpc(collections, options)”Creates a typed tRPC router from procedure collections.
import { rpc } from '@veloxts/velox';
const { router, register } = rpc(collections, { prefix: '/trpc', // Endpoint prefix (default: '/trpc')});
// router: Fully typed tRPC router for type export// register: Async function to register with FastifyregisterRpc(app, collections, options)
Section titled “registerRpc(app, collections, options)”Convenience function that combines router creation and registration:
import { registerRpc } from '@veloxts/velox';
// One-liner registrationconst router = await registerRpc(app, collections, { prefix: '/trpc' });export type AppRouter = typeof router;serve(app, collections, options)
Section titled “serve(app, collections, options)”Unified function for both REST and tRPC:
import { serve } from '@veloxts/velox';
// Both REST and tRPCconst router = await serve(app, collections, { api: '/api', // REST prefix (false to disable) rpc: '/trpc', // tRPC prefix (false to disable)});
// tRPC onlyconst router = await serve(app, collections, { api: false });
// REST onlyawait serve(app, collections, { rpc: false });Client Setup
Section titled “Client Setup”import { createClient } from '@veloxts/client';import type { AppRouter } from '../../api/src/router.js';
// Works with tRPC router types from rpc()export const api = createClient<AppRouter>({ baseUrl: 'http://localhost:3030/trpc', // mode: 'trpc' - auto-detected when baseUrl ends with /trpc});
// Fully typed!const users = await api.users.listUsers();const user = await api.users.getUser({ id: '123' });await api.users.createUser({ name: 'Alice', email: 'alice@example.com' });import { createVeloxHooks } from '@veloxts/client/react';import type { AppRouter } from '../../api/src/router.js';
// Works with tRPC router types from rpc()export const api = createVeloxHooks<AppRouter>();
// In componentfunction UserList() { const { data } = api.users.listUsers.useQuery(); return <ul>{data?.data.map(u => <li key={u.id}>{u.name}</li>)}</ul>;}
function UserProfile({ userId }: { userId: string }) { // Suspense variant const { data: user } = api.users.getUser.useSuspenseQuery({ id: userId }); return <h1>{user.name}</h1>;}import { createTRPCClient, httpBatchLink } from '@trpc/client';import type { AppRouter } from '../server/router';
export const trpc = createTRPCClient<AppRouter>({ links: [ httpBatchLink({ url: 'http://localhost:3030/trpc', }), ],});
// Fully typed!const users = await trpc.users.listUsers.query();const user = await trpc.users.getUser.query({ id: '123' });await trpc.users.createUser.mutate({ name: 'Alice', email: 'alice@example.com' });Type Inference
Section titled “Type Inference”Types flow automatically from backend to frontend:
// Backend defines the shapeconst createUser = procedure() .input(z.object({ name: z.string(), email: z.string().email(), })) .mutation(...)
// Frontend gets the typestrpc.users.createUser.mutate({ name: 'Alice', // TypeScript knows this is required email: 'alice@example.com', // @ts-error - 'age' doesn't exist age: 30,});Batching
Section titled “Batching”tRPC automatically batches multiple queries:
// These are batched into a single HTTP requestconst [users, posts] = await Promise.all([ trpc.users.listUsers.query(), trpc.posts.listPosts.query(),]);Error Handling
Section titled “Error Handling”tRPC errors are automatically formatted:
try { await trpc.users.getUser.query({ id: 'invalid' });} catch (error) { if (error.data?.code === 'NOT_FOUND') { // Handle not found }}tRPC vs REST
Section titled “tRPC vs REST”| Feature | tRPC | REST |
|---|---|---|
| Type safety | Full inference | Manual typing |
| Batching | Automatic | Manual |
| Caching | React Query | HTTP cache |
| External clients | TypeScript only | Any language |
| OpenAPI docs | No | Yes |
Related Content
Section titled “Related Content”- Client Package - Velox TS client
- REST Conventions - REST endpoints
- Procedures - Define procedures