trpc-fullstack – skill Lập trình cho agent AI
Xây dựng API type-safe đầu cuối với tRPC: router, procedure, middleware, subscription và các mẫu tích hợp Next.js/React.
Cài skill trpc-fullstack bằng một lệnh
Chọn công cụ bạn đang dùng, copy lệnh và dán vào terminal. Skill sẽ nằm trong thư mục skills của công cụ đó và sẵn sàng dùng ở phiên làm việc tiếp theo.
Claude Code
mkdir -p ~/.claude/skills && curl -fsSL https://skill.dn1nguoi.com/z/trpc-fullstack-9839c.tgz | tar xz -C ~/.claude/skills
Gọi trong Claude Code: /trpc-fullstack – rồi mô tả công việc bạn muốn làm.
Codex
mkdir -p ~/.codex/skills && curl -fsSL https://skill.dn1nguoi.com/z/trpc-fullstack-9839c.tgz | tar xz -C ~/.codex/skills
Gọi trong Codex: $trpc-fullstack – rồi mô tả công việc bạn muốn làm.
Hermes Agent
mkdir -p ~/.hermes/skills && curl -fsSL https://skill.dn1nguoi.com/z/trpc-fullstack-9839c.tgz | tar xz -C ~/.hermes/skills
Gọi trong Hermes Agent: /trpc-fullstack – rồi mô tả công việc bạn muốn làm.
DeepSeek
mkdir -p ~/.deepcode/skills && curl -fsSL https://skill.dn1nguoi.com/z/trpc-fullstack-9839c.tgz | tar xz -C ~/.deepcode/skills
Gọi trong DeepSeek: /trpc-fullstack – rồi mô tả công việc bạn muốn làm.
Nội dung SKILL.md
Nội dung SKILL.md gốc bằng tiếng Anh.
tRPC Full-Stack
Overview
tRPC lets you build fully type-safe APIs without writing a schema or code-generation step. Your TypeScript types flow from the server router directly to the client — so every API call is autocompleted, validated at compile time, and refactoring-safe. Use this skill when building TypeScript monorepos, Next.js apps, or any project where the server and client share a codebase.
When to Use This Skill
- Use when building a TypeScript full-stack app (Next.js, Remix, Express + React) where the client and server share a single repo
- Use when you want end-to-end type safety on API calls without REST/GraphQL schema overhead
- Use when adding real-time features (subscriptions) to an existing tRPC setup
- Use when designing multi-step middleware (auth, rate limiting, tenant scoping) on tRPC procedures
- Use when migrating an existing REST/GraphQL API to tRPC incrementally
Examples require matching tRPC/TanStack Query versions and application-specific auth, database models and user/session types. Cron jobs cannot assume request-bound auth() exists: build an explicitly authorized service context. The shown subscription router also needs root registration, a transport adapter and client link; its process-local emitter does not distribute across replicas. HTTP GET/POST handlers do not alone provide WebSockets. Type safety is not authorization: scope queries/output fields to permitted records, handle CSRF/origin for cookie-authenticated mutations, and enforce ownership atomically with writes.
Core Concepts
Routers and Procedures
A router groups related procedures (think: endpoints). Procedures are typed functions — query for reads, mutation for writes, subscription for real-time streams.
Input Validation with Zod
All procedure inputs are validated with Zod schemas. The validated, typed input is available in the procedure handler — no manual parsing.
Context
context is shared state passed to every procedure — auth session, database client, request headers, etc. It is built once per request in a context factory. Important: Next.js App Router and Pages Router require separate context factories because App Router handlers receive a fetch Request, not a Node.js NextApiRequest.
Middleware
Middleware chains run before a procedure. Use them for authentication, logging, and request enrichment. They can extend the context for downstream procedures.
---
How It Works
Step 1: Install and Initialize
npm install @trpc/server @trpc/client @trpc/react-query @tanstack/react-query zodCreate the tRPC instance and reusable builders:
// src/server/trpc.ts
import { initTRPC, TRPCError } from '@trpc/server';
import { type Context } from './context';
import { ZodError } from 'zod';
const t = initTRPC.context<Context>().create({
errorFormatter({ shape, error }) {
return {
...shape,
data: {
...shape.data,
zodError:
error.cause instanceof 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;Step 2: Define Two Context Factories
Next.js App Router handlers receive a fetch Request (not a Node.js NextApiRequest), so the context
must be built differently depending on the call site. Define one factory per surface:
// src/server/context.ts
import { type FetchCreateContextFnOptions } from '@trpc/server/adapters/fetch';
import { auth } from '@/server/auth'; // Next-Auth v5 / your auth helper
import { db } from './db';
/**
* Context for the HTTP handler (App Router Route Handler).
* `opts.req` is the fetch Request — auth is resolved server-side via `auth()`.
*/
export async function createTRPCContext(opts: FetchCreateContextFnOptions) {
const session = await auth(); // server-side auth — no req/res needed
return { session, db, headers: opts.req.headers };
}
/**
* Context for direct server-side callers (Server Components, RSC, cron jobs).
* No HTTP request is involved, so we call auth() directly from the server.
*/
export async function createServerContext() {
const session = await auth();
return { session, db, headers: new Headers() }; // matches Context; no HTTP request metadata
}
export type Context = Awaited<ReturnType<typeof createTRPCContext>>;Step 3: Build an Auth Middleware and Protected Procedure
// src/server/trpc.ts (continued)
const enforceAuth = middleware(({ ctx, next }) => {
if (!ctx.session?.user) {
throw new TRPCError({ code: 'UNAUTHORIZED' });
}
return next({
ctx: {
// Narrows type: session is non-null from here
session: { ...ctx.session, user: ctx.session.user },
},
});
});
export const protectedProcedure = t.procedure.use(enforceAuth);Step 4: Create Routers
// src/server/routers/post.ts
import { z } from 'zod';
import { router, publicProcedure, protectedProcedure } from '../trpc';
import { TRPCError } from '@trpc/server';
export const postRouter = router({
list: publicProcedure
.input(
z.object({
limit: z.number().int().min(1).max(100).default(20),
cursor: z.string().optional(),
})
)
.query(async ({ ctx, input }) => {
const posts = await ctx.db.post.findMany({
take: input.limit + 1,
cursor: input.cursor ? { id: input.cursor } : undefined,
orderBy: [{ createdAt: 'desc' }, { id: 'desc' }],
});
const nextCursor =
posts.length > input.limit ? posts.pop()!.id : undefined;
return { posts, nextCursor };
}),
byId: publicProcedure
.input(z.object({ id: z.string() }))
.query(async ({ ctx, input }) => {
const post = await ctx.db.post.findUnique({ where: { id: input.id } });
if (!post) throw new TRPCError({ code: 'NOT_FOUND' });
return post;
}),
create: protectedProcedure
.input(
z.object({
title: z.string().min(1).max(200),
body: z.string().min(1),
})
)
.mutation(async ({ ctx, input }) => {
return ctx.db.post.create({
data: { ...input, authorId: ctx.session.user.id },
});
}),
delete: protectedProcedure
.input(z.object({ id: z.string() }))
.mutation(async ({ ctx, input }) => {
const post = await ctx.db.post.findUnique({ where: { id: input.id } });
if (!post) throw new TRPCError({ code: 'NOT_FOUND' });
if (post.authorId !== ctx.session.user.id)
throw new TRPCError({ code: 'FORBIDDEN' });
return ctx.db.post.delete({ where: { id: input.id } });
}),
});Step 5: Compose the Root Router and Export Types
// src/server/root.ts
import { router } from './trpc';
import { postRouter } from './routers/post';
import { userRouter } from './routers/user';
export const appRouter = router({
post: postRouter,
user: userRouter,
});
// Export the type for the client — never import the appRouter itself on the client
export type AppRouter = typeof appRouter;Step 6: Mount the API Handler (Next.js App Router)
The App Router handler must use fetchRequestHandler and the fetch-based context factory.
createTRPCContext receives FetchCreateContextFnOptions (with a fetch Request), not
a Pages Router req/res pair.
// src/app/api/trpc/[trpc]/route.ts
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
import { type FetchCreateContextFnOptions } from '@trpc/server/adapters/fetch';
import { appRouter } from '@/server/root';
import { createTRPCContext } from '@/server/context';
const handler = (req: Request) =>
fetchRequestHandler({
endpoint: '/api/trpc',
req,
router: appRouter,
// opts is FetchCreateContextFnOptions — req is the fetch Request
createContext: (opts: FetchCreateContextFnOptions) => createTRPCContext(opts),
});
export { handler as GET, handler as POST };Step 7: Set Up the Client (React Query)
// src/utils/trpc.ts
import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from '@/server/root';
export const trpc = createTRPCReact<AppRouter>();// src/app/providers.tsx
'use client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { httpBatchLink } from '@trpc/client';
import { useState } from 'react';
import { trpc } from '@/utils/trpc';
export function TRPCProvider({ children }: { children: React.ReactNode }) {
const [queryClient] = useState(() => new QueryClient());
const [trpcClient] = useState(() =>
trpc.createClient({
links: [
httpBatchLink({
url: '/api/trpc',
headers: () => ({ 'x-trpc-source': 'react' }),
}),
],
})
);
return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
… (nội dung đầy đủ có trong gói tải về)
Skill liên quan
- twilio-communications – Xây dựng tính năng giao tiếp với Twilio: nhắn tin SMS, cuộc gọi thoại, WhatsApp Business…
- typescript – Bộ quy tắc viết code theo ngôn ngữ cho TypeScript.
- typescript-advanced-types – Hướng dẫn chuyên sâu về hệ thống kiểu nâng cao của TypeScript, gồm generic, kiểu có điều…
- typescript-expert – Chuyên gia TypeScript và JavaScript, am hiểu sâu về lập trình ở cấp độ kiểu…
- typescript-pro – Làm chủ TypeScript với kiểu nâng cao, generics và an toàn kiểu nghiêm ngặt. Xử lý hệ…
- uncle-bob-craft – Dùng khi review code, viết hoặc refactor code, hoặc thảo luận về kiến trúc; bổ sung cho…
- understand-project-yylo – Kiểm tra kiến trúc sản phẩm, dependency và vòng lặp xác thực hiện tại trước khi lập kế…
- unit-testing-test-generate – Tạo unit test toàn diện, dễ bảo trì cho nhiều ngôn ngữ, với độ phủ cao và tập trung vào…