Build fully type-safe APIs with tRPC, combining REST and GraphQL concepts without code generation. Define routers, procedures, and inputs with automatic TypeScript inference across client and server.
Build fully type-safe APIs with tRPC that combine concepts from REST and GraphQL without code generation. This skill guides you through creating type-safe routers, procedures, and client integrations.
This skill helps you:
Install the required tRPC packages in the appropriate sections of your codebase:
```bash
npm install @trpc/server @trpc/client
```
For React integration, also install `@trpc/react-query`. For Next.js, see framework-specific guides.
Create a `server/trpc.ts` file to initialize tRPC (do this only once):
```typescript
import { initTRPC } from '@trpc/server';
/**
* Initialization of tRPC backend
* Should be done only once per backend!
*/
const t = initTRPC.create();
/**
* Export reusable router and procedure helpers
* that can be used throughout the router
*/
export const router = t.router;
export const publicProcedure = t.procedure;
```
Create a `server/index.ts` file with your main router:
```typescript
import { router } from './trpc';
const appRouter = router({
// ... procedures will go here
});
// Export type router type signature, NOT the router itself
export type AppRouter = typeof appRouter;
```
**Important:** Export the *type* of the router, not the router instance itself.
Add query procedures using `publicProcedure.query()`:
```typescript
import { db } from './db';
import { publicProcedure, router } from './trpc';
const appRouter = router({
userList: publicProcedure.query(async () => {
// Retrieve users from your datasource
const users = await db.user.findMany();
return users;
}),
});
```
Use `.input()` to validate procedure inputs with Zod or custom validators:
**With Zod (recommended):**
```typescript
import { z } from 'zod';
const appRouter = router({
userById: publicProcedure
.input(z.string())
.query(async (opts) => {
const { input } = opts; // input is typed as string
const user = await db.user.findById(input);
return user;
}),
});
```
**With vanilla JavaScript validation:**
```typescript
const appRouter = router({
userById: publicProcedure
.input((val: unknown) => {
if (typeof val === 'string') return val;
throw new Error(`Invalid input: ${typeof val}`);
})
.query(async (opts) => {
const { input } = opts;
const user = await db.user.findById(input);
return user;
}),
});
```
Use `.mutation()` for operations that modify data:
```typescript
const appRouter = router({
userCreate: publicProcedure
.input(z.object({ name: z.string() }))
.mutation(async (opts) => {
const { input } = opts;
const user = await db.user.create(input);
return user;
}),
});
```
Create a typed tRPC client using the exported router type:
```typescript
import { createTRPCClient, httpBatchLink } from '@trpc/client';
import type { AppRouter } from './server';
const client = createTRPCClient<AppRouter>({
links: [
httpBatchLink({
url: 'http://localhost:3000/trpc',
}),
],
});
```
Use the typed client with full IntelliSense:
```typescript
// Query
const users = await client.userList.query();
// Query with input
const user = await client.userById.query('user-id-123');
// Mutation
const newUser = await client.userCreate.mutate({ name: 'John Doe' });
```
```typescript
import { z } from 'zod';
import { router, publicProcedure } from './trpc';
import { db } from './db';
export const appRouter = router({
// List all users
userList: publicProcedure.query(async () => {
return await db.user.findMany();
}),
// Get user by ID
userById: publicProcedure
.input(z.string())
.query(async ({ input }) => {
return await db.user.findById(input);
}),
// Create new user
userCreate: publicProcedure
.input(z.object({ name: z.string() }))
.mutation(async ({ input }) => {
return await db.user.create(input);
}),
});
export type AppRouter = typeof appRouter;
```
Based on tRPC Quickstart documentation: https://trpc.io/docs/quickstart
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/trpc-end-to-end-type-safe-apis/raw