tRPC API Architecture
The tRPC architecture skips REST endpoint generation entirely and exposes your procedures as type-safe RPC calls. Your frontend gets full autocompletion, compile-time error checking, and zero runtime overhead from schema serialization. Choose this when every client is TypeScript and you don’t need REST or OpenAPI.
When to Choose tRPC
Section titled “When to Choose tRPC”- All clients are TypeScript
- Maximum type safety is priority
- Internal APIs between services
- No need for REST/OpenAPI
Project Setup
Section titled “Project Setup”npx create-velox-app my-api --trpcHow It Works
Section titled “How It Works”With --trpc, Velox TS skips REST generation. You get pure tRPC:
import { procedures, procedure, resourceSchema, resource, resourceCollection } from '@veloxts/velox';import { z } from '@veloxts/velox';
// Define resource schema with field visibilityconst UserSchema = resourceSchema() .public('id', z.string()) .public('name', z.string()) .authenticated('email', z.string()) .admin('lastLoginAt', z.date().nullable()) .build();
export const userProcedures = procedures('users', { list: procedure() .query(async ({ ctx }) => { const users = await ctx.db.user.findMany(); return resourceCollection(users, UserSchema.authenticated); }),
get: procedure() .input(z.object({ id: z.string() })) .query(async ({ input, ctx }) => { const user = await ctx.db.user.findUniqueOrThrow({ where: { id: input.id } }); return resource(user, UserSchema.authenticated); }),
create: procedure() .input(CreateUserSchema) .mutation(async ({ input, ctx }) => { const user = await ctx.db.user.create({ data: input }); return resource(user, UserSchema.authenticated); }),});Client Usage
Section titled “Client Usage”Use @veloxts/client in tRPC mode for fully typed API calls:
import { createClient } from '@veloxts/client';import type { AppRouter } from '../../api/src/router';
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.list();const user = await api.users.get({ id: '123' });The client auto-detects tRPC mode when baseUrl ends with /trpc. Queries (procedures starting with get, list, find) use GET with encoded input, mutations use POST with JSON body.
React Query Integration
Section titled “React Query Integration”For React apps, use the hooks API:
import { createVeloxHooks, VeloxProvider } from '@veloxts/client/react';import type { AppRouter } from '@/api';
export const api = createVeloxHooks<AppRouter>();
// In your appfunction UserList() { const { data: users } = api.users.list.useQuery(); return <ul>{users?.map(u => <li key={u.id}>{u.name}</li>)}</ul>;}See Client Package for full React integration.
Using @trpc/react-query (Advanced)
Section titled “Using @trpc/react-query (Advanced)”For existing codebases or specific tRPC features, use TRPCRouter to bridge types:
import { createTRPCReact } from '@trpc/react-query';import type { TRPCRouter } from '@veloxts/router';import type { AppRouter } from '../../api/src/router';
export const trpc = createTRPCReact<TRPCRouter<AppRouter>>();For most Velox TS apps, @veloxts/client is recommended.
Type Inference
Section titled “Type Inference”Types flow automatically:
// Backend defines the shapeconst createUser = procedure() .input(z.object({ name: z.string(), email: z.string().email(), })) .mutation(...)
// Frontend gets the typestrpc.users.create.mutate({ name: 'Alice', // TypeScript knows this is required email: 'alice@example.com', // @ts-error - 'age' doesn't exist age: 30,});Adding REST Later
Section titled “Adding REST Later”If you later need REST endpoints (for mobile apps, third parties, webhooks), add the REST adapter to your app:
import { veloxApp, rest } from '@veloxts/velox';import { collections } from './router.js';
const app = await veloxApp({ port: 3030 });
app.routes( rest(collections, { prefix: '/api' }) // Add this line);REST uses naming conventions to infer HTTP methods, so you may need to rename procedures:
// Before (tRPC-style, short names)list: procedure()...get: procedure()...create: procedure()...
// After (REST-style, includes resource name)listUsers: procedure()... // → GET /api/usersgetUser: procedure()... // → GET /api/users/:idcreateUser: procedure()... // → POST /api/usersSee REST Conventions for the full naming guide.
Related Content
Section titled “Related Content”- Procedures - Procedure API reference
- tRPC Adapter - tRPC configuration
- Client Package - Frontend client