tRPC API Architecture
Use this architecture when all your clients are TypeScript and you want maximum type safety.
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, VeloxTS skips REST generation. You get pure tRPC:
import { procedures, procedure } from '@veloxts/velox';import { z } from '@veloxts/velox';
export const userProcedures = procedures('users', { list: procedure() .output(z.array(UserSchema)) .query(({ ctx }) => ctx.db.user.findMany()),
get: procedure() .input(z.object({ id: z.string() })) .query(({ input, ctx }) => ctx.db.user.findUniqueOrThrow({ where: { id: input.id } })),
create: procedure() .input(CreateUserSchema) .mutation(({ input, ctx }) => ctx.db.user.create({ data: input })),});Client Usage
Section titled “Client Usage”The frontend imports types directly from the backend:
import { createTRPCClient } from '@trpc/client';import type { AppRouter } from '../../api/src/router';
export const trpc = createTRPCClient<AppRouter>({ links: [ httpBatchLink({ url: 'http://localhost:3030/trpc', }), ],});
// Fully typed!const users = await trpc.users.list.query();const user = await trpc.users.get.query({ id: '123' });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.
Next Steps
Section titled “Next Steps”- Procedures - Procedure API reference
- tRPC Adapter - tRPC configuration
- Client Package - Frontend client