xiaohongshu
xiaohongshu
backend-trpc
Type-safe API layer for TypeScript full-stack applications. Use when building APIs that need end-to-end type safety between client and server WITHOUT code generation. Ideal for Next.js, React, and Express apps where both frontend and backend are TypeScript. Choose tRPC over REST/GraphQL when you ...
Full skill instructions
tRPC enables end-to-end typesafe APIs by sharing TypeScript types between client and server. No code generation, no schema files—just TypeScript.
Version: v11.7+ (2024-2025)
Requirements: TypeScript ≥5.7.2 with strict mode
Key Benefit: Change a procedure's input/output → TypeScript errors appear immediately on client.
✅ Use tRPC when:
❌ Don't use tRPC when:
npm install @trpc/server @trpc/client zod
# For React/Next.js:
npm install @trpc/react-query @tanstack/react-query@^5
Always create tRPC instance in a dedicated file:
// src/server/trpc.ts
import { initTRPC, TRPCError } from '@trpc/server';
import { z } from 'zod';
interface Context {
user?: { id: string; role: string };
db: PrismaClient;
}
const t = initTRPC.context<Context>().create({
errorFormatter({ shape, error }) {
return {
...shape,
data: {
...shape.data,
zodError: error.cause instanceof z.ZodError
? error.cause.flatten()
: null,
},
};
},
});
export const router = t.router;
export const publicProcedure = t.procedure;
export const middleware = t.middleware;
export const createCallerFactory = t.createCallerFactory;
// src/server/routers/user.ts
import { z } from 'zod';
import { router, publicProcedure, protectedProcedure } from '../trpc';
import { TRPCError } from '@trpc/server';
export const userRouter = router({
// Query - GET semantics (reads)
getById: publicProcedure
.input(z.object({ id: z.string().uuid() }))
.query(async ({ input, ctx }) => {
const user = await ctx.db.user.findUnique({ where: { id: input.id } });
if (!user) throw new TRPCError({ code: 'NOT_FOUND' });
return user;
}),
// Mutation - POST/PUT/DELETE semantics (writes)
create: protectedProcedure
.input(z.object({
name: z.string().min(2).max(100),
email: z.string().email(),
}))
.mutation(async ({ input, ctx }) => {
return ctx.db.user.create({ data: input });
}),
});
list: publicProcedure
.input(z.object({
limit: z.number().min(1).max(100).default(10),
cursor: z.string().uuid().optional(),
}))
.query(async ({ input, ctx }) => {
const items = await ctx.db.user.findMany({
take: input.limit + 1,
cursor: input.cursor ? { id: input.cursor } : undefined,
orderBy: { createdAt: 'desc' },
});
let nextCursor: string | undefined;
if (items.length > input.limit) {
nextCursor = items.pop()?.id;
}
return { items, nextCursor };
}),
const isAuthed = middleware(async ({ ctx, next }) => {
if (!ctx.user) {
throw new TRPCError({ code: 'UNAUTHORIZED' });
}
return next({ ctx: { user: ctx.user } });
});
export const protectedProcedure = publicProcedure.use(isAuthed);
const hasRole = (role: string) => middleware(async ({ ctx, next }) => {
if (ctx.user?.role !== role) {
throw new TRPCError({ code: 'FORBIDDEN' });
}
return next();
});
export const adminProcedure = protectedProcedure.use(hasRole('admin'));
const loggerMiddleware = middleware(async ({ path, type, next }) => {
const start = Date.now();
const result = await next();
console.log(`[${type}] ${path} - ${Date.now() - start}ms`);
return result;
});
// src/server/context.ts
import { CreateExpressContextOptions } from '@trpc/server/adapters/express';
import { prisma } from '../lib/prisma';
export async function createContext({ req }: CreateExpressContextOptions) {
const token = req.headers.authorization?.split(' ')[1];
const user = token ? await verifyToken(token) : null;
return { user, db: prisma };
}
export type Context = Awaited<ReturnType<typeof createContext>>;
// src/server/index.ts
import express from 'express';
import cors from 'cors';
import { createExpressMiddleware } from '@trpc/server/adapters/express';
import { appRouter } from './routers/_app';
import { createContext } from './context';
const app = express();
app.use(cors());
app.use('/trpc', createExpressMiddleware({
router: appRouter,
createContext,
}));
app.listen(3000);
// src/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;
TRPCError with appropriate codeshttpBatchLink on client for request batchingAppRouter type for clientany for context types| Code | HTTP | Use Case |
|---|---|---|
BAD_REQUEST | 400 | Invalid input |
UNAUTHORIZED | 401 | No/invalid auth |
FORBIDDEN | 403 | No permission |
NOT_FOUND | 404 | Resource missing |
CONFLICT | 409 | Already exists |
INTERNAL_SERVER_ERROR | 500 | Unexpected error |
"Types not updating on client":
→ Run TypeScript server in watch mode
→ Check AppRouter is exported and imported correctly
→ Verify tsconfig paths match
"Input validation errors not showing":
→ Add zodError to errorFormatter
→ Use .safeParse() on client for detailed errors
"CORS errors":
→ Configure cors() before tRPC middleware
→ Check origin whitelist
"Procedures not batching":
→ Ensure using httpBatchLink on client
→ Check all requests go to same endpoint
src/server/
├── trpc.ts # tRPC instance, base procedures
├── context.ts # Context creation
└── routers/
├── _app.ts # Root router (merges all)
├── user.ts # User procedures
└── post.ts # Post procedures
xiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience