Skip to content

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.

Use the rpc() helper to create a typed tRPC router:

src/router.ts
import { rpc } from '@veloxts/velox';
import { userProcedures } from './procedures/users';
import { postProcedures } from './procedures/posts';
// IMPORTANT: Use `as const` for full type inference
export const collections = [userProcedures, postProcedures] as const;
// Create typed tRPC router
const { router } = rpc(collections, { prefix: '/trpc' });
export { router };
export type AppRouter = typeof router;

Register with your app:

src/index.ts
import { veloxApp, registerRpc, rest } from '@veloxts/velox';
import { collections } from './router.js';
const app = await veloxApp({ port: 3030 });
// Register tRPC at /trpc
await registerRpc(app, collections, { prefix: '/trpc' });
// Optionally register REST at /api
app.routes(rest([...collections], { prefix: '/api' }));
await app.start();

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 Fastify

Convenience function that combines router creation and registration:

import { registerRpc } from '@veloxts/velox';
// One-liner registration
const router = await registerRpc(app, collections, { prefix: '/trpc' });
export type AppRouter = typeof router;

Unified function for both REST and tRPC:

import { serve } from '@veloxts/velox';
// Both REST and tRPC
const router = await serve(app, collections, {
api: '/api', // REST prefix (false to disable)
rpc: '/trpc', // tRPC prefix (false to disable)
});
// tRPC only
const router = await serve(app, collections, { api: false });
// REST only
await serve(app, collections, { rpc: false });
lib/api.ts
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' });

Types flow automatically from backend to frontend:

// Backend defines the shape
const createUser = procedure()
.input(z.object({
name: z.string(),
email: z.string().email(),
}))
.mutation(...)
// Frontend gets the types
trpc.users.createUser.mutate({
name: 'Alice', // TypeScript knows this is required
email: 'alice@example.com',
// @ts-error - 'age' doesn't exist
age: 30,
});

tRPC automatically batches multiple queries:

// These are batched into a single HTTP request
const [users, posts] = await Promise.all([
trpc.users.listUsers.query(),
trpc.posts.listPosts.query(),
]);

tRPC errors are automatically formatted:

try {
await trpc.users.getUser.query({ id: 'invalid' });
} catch (error) {
if (error.data?.code === 'NOT_FOUND') {
// Handle not found
}
}
FeaturetRPCREST
Type safetyFull inferenceManual typing
BatchingAutomaticManual
CachingReact QueryHTTP cache
External clientsTypeScript onlyAny language
OpenAPI docsNoYes