Skip to content
Back to skills

Trpc

ASecurity

Guidelines for writing Next.js apps with tRPC v11 for end-to-end typesafe APIs

  • 248 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added February 10, 2026
developmentjavascripttypescriptjavareactnextjsapidatabasebackendperformance

Works with

  • cli
  • api

Security analysis

A100/100

Scanned February 10, 2026

npx -y skills add Mindrally/skills --skill trpc --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Trpc?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Trpc
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/mindrally-trpc/badge)](https://www.skillsdirectory.com/skills/mindrally-trpc)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: trpc
description: Guidelines for writing Next.js apps with tRPC v11 for end-to-end typesafe APIs
---

# tRPC Best Practices

You are an expert in tRPC v11, TypeScript, and Next.js development. tRPC enables end-to-end typesafe APIs, allowing you to build and consume APIs without schemas, code generation, or runtime errors.

## Requirements

- TypeScript >= 5.7.2
- Strict TypeScript mode enabled

## Project Structure

```
src/
  pages/
    _app.tsx              # createTRPCNext setup
    api/
      trpc/
        [trpc].ts         # tRPC HTTP handler
  server/
    routers/
      _app.ts             # Main router
      [feature].ts        # Feature-specific routers
    context.ts            # App context
    trpc.ts               # Procedure helpers
  utils/
    trpc.ts               # Typesafe React hooks
```

## Server-Side Setup

### Initialize tRPC Backend

Initialize tRPC backend once per application. Export reusable router and procedure helpers:

```typescript
// server/trpc.ts
import { initTRPC } from '@trpc/server';
import superjson from 'superjson';

const t = initTRPC.context<Context>().create({
  transformer: superjson,
});

export const router = t.router;
export const publicProcedure = t.procedure;
export const protectedProcedure = t.procedure.use(isAuthed);
export const adminProcedure = t.procedure.use(isAdmin);
```

### Create Feature Routers

Organize routers by feature/domain. Use Zod for input validation:

```typescript
// server/routers/user.ts
import { z } from 'zod';
import { router, publicProcedure, protectedProcedure } from '../trpc';

export const userRouter = router({
  getById: publicProcedure
    .input(z.object({ id: z.string() }))
    .query(async ({ input, ctx }) => {
      return ctx.db.user.findUnique({ where: { id: input.id } });
    }),

  update: protectedProcedure
    .input(z.object({
      name: z.string().min(1),
      email: z.string().email(),
    }))
    .mutation(async ({ input, ctx }) => {
      return ctx.db.user.update({
        where: { id: ctx.user.id },
        data: input,
      });
    }),
});
```

### Main App Router

```typescript
// server/routers/_app.ts
import { router } from '../trpc';
import { userRouter } from './user';
import { postRouter } from './post';

export const appRouter = router({
  user: userRouter,
  post: postRouter,
});

export type AppRouter = typeof appRouter;
```

## Client-Side Setup

### Configure tRPC Client

```typescript
// utils/trpc.ts
import { httpBatchLink } from '@trpc/client';
import { createTRPCNext } from '@trpc/next';
import superjson from 'superjson';
import type { AppRouter } from '../server/routers/_app';

function getBaseUrl() {
  if (typeof window !== 'undefined') return '';
  if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`;
  return `http://localhost:${process.env.PORT ?? 3000}`;
}

export const trpc = createTRPCNext<AppRouter>({
  config() {
    return {
      transformer: superjson,
      links: [
        httpBatchLink({
          url: `${getBaseUrl()}/api/trpc`,
          maxURLLength: 2083,
        }),
      ],
    };
  },
  ssr: false,
});
```

## Key Best Practices

### 1. Input Validation
Always use Zod for type safety and runtime validation on all procedure inputs:

```typescript
.input(z.object({
  email: z.string().email(),
  age: z.number().min(0).max(120),
}))
```

### 2. Router Organization
Structure routers by feature/domain rather than one monolithic router. Each feature should have its own router file.

### 3. Middleware Implementation
Implement middleware for authentication, logging, and cross-cutting concerns:

```typescript
const isAuthed = t.middleware(({ ctx, next }) => {
  if (!ctx.user) {
    throw new TRPCError({ code: 'UNAUTHORIZED' });
  }
  return next({ ctx: { user: ctx.user } });
});
```

### 4. Error Handling
Use TRPCError for consistent, informative error responses:

```typescript
import { TRPCError } from '@trpc/server';

throw new TRPCError({
  code: 'NOT_FOUND',
  message: 'User not found',
});
```

### 5. Data Transformers
Apply SuperJSON for automatic serialization of dates, Maps, Sets, and other JavaScript types:

```typescript
import superjson from 'superjson';

const t = initTRPC.create({
  transformer: superjson,
});
```

### 6. React Query Integration
Leverage tRPC's built-in React Query utilities for data fetching, mutations, and caching:

```typescript
// Queries
const { data, isLoading } = trpc.user.getById.useQuery({ id: '123' });

// Mutations
const mutation = trpc.user.update.useMutation({
  onSuccess: () => {
    utils.user.getById.invalidate();
  },
});

// Prefetching
await utils.user.getById.prefetch({ id: '123' });
```

### 7. Context Creation
Share resources (database connections, user sessions) across procedures via context:

```typescript
// server/context.ts
export async function createContext({ req, res }: CreateNextContextOptions) {
  const user = await getUser(req);
  return {
    db: prisma,
    user,
    req,
    res,
  };
}

export type Context = Awaited<ReturnType<typeof createContext>>;
```

### 8. Type Exports
Export only type signatures to client code, never router implementations:

```typescript
// Only export the type
export type AppRouter = typeof appRouter;

// Never export the actual router to client code
```

### 9. Authorization Levels
Create distinct procedure types for different authorization levels:

```typescript
export const publicProcedure = t.procedure;
export const protectedProcedure = t.procedure.use(isAuthed);
export const adminProcedure = t.procedure.use(isAuthed).use(isAdmin);
```

### 10. Performance Optimization
- Enable request batching with httpBatchLink
- Configure maxURLLength to prevent URL length issues
- Implement prefetching for static/predictable data
- Use React Query's caching strategies

## Anti-Patterns to Avoid

- Do not use `any` types - leverage tRPC's full type inference
- Do not skip input validation
- Do not expose internal errors to clients
- Do not mix server and client code
- Do not create overly large routers - split by feature

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…