بنقرة واحدة
trpc-patterns
tRPC v11: Type-safe API layer, routers, procedures, context, middleware, React Query integration.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
tRPC v11: Type-safe API layer, routers, procedures, context, middleware, React Query integration.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Bun runtime: HTTP server, file I/O, SQLite, test runner, package manager, bundler — all-in-one JS toolchain.
Clerk: Drop-in auth UI, Organizations, User management, JWT templates, webhooks, Next.js middleware integration.
Gelişmiş masaüstü, tarayıcı ve işletim sistemi kontrol yeteneği. Görsel (koordinat tabanlı) fare/klavye otomasyonu, DOM manipülasyonu, pencere yönetimi, gelişmiş dosya, ağ ve süreç yönetimini kapsar.
Drizzle ORM: Schema definition, type-safe queries, migrations, relations, Postgres/SQLite/MySQL support.
Expo Router v3: File-based navigation, layouts, tabs, modals, deep linking, API routes, typed routes.
Flutter ile oyun geliştirme (Flame Engine vb.) ve karmaşık, büyük ölçekli mimariler kurma rehberi.
| name | trpc-patterns |
| description | tRPC v11: Type-safe API layer, routers, procedures, context, middleware, React Query integration. |
| triggers | {"extensions":[".ts",".tsx"],"directories":["server/trpc/","trpc/","src/server/"],"filenames":["trpc.ts","router.ts","_app.ts"],"keywords":["trpc","createTRPCRouter","publicProcedure","protectedProcedure","createCallerFactory","inferAsyncReturnType"]} |
| auto_load_when | Building full-stack TypeScript API with tRPC |
| agent | architect |
| tools | ["Read","Write","Bash"] |
Version: tRPC v11 | Stack: Next.js App Router + React Query
// server/trpc/trpc.ts — core setup
import { initTRPC, TRPCError } from '@trpc/server';
import { type Session } from 'next-auth';
import superjson from 'superjson';
import { ZodError } from 'zod';
import { db } from '@/lib/db';
export const createTRPCContext = async (opts: { headers: Headers }) => {
const session = await getServerSession();
return { db, session, ...opts };
};
const t = initTRPC.context<typeof createTRPCContext>().create({
transformer: superjson,
errorFormatter({ shape, error }) {
return {
...shape,
data: {
...shape.data,
zodError: error.cause instanceof ZodError ? error.cause.flatten() : null,
},
};
},
});
export const createTRPCRouter = t.router;
export const publicProcedure = t.procedure;
export const protectedProcedure = t.procedure.use(({ ctx, next }) => {
if (!ctx.session?.user) throw new TRPCError({ code: 'UNAUTHORIZED' });
return next({ ctx: { ...ctx, session: ctx.session } });
});
// server/trpc/routers/post.ts
import { z } from 'zod';
import { createTRPCRouter, protectedProcedure, publicProcedure } from '../trpc';
export const postRouter = createTRPCRouter({
getAll: publicProcedure
.input(z.object({ cursor: z.string().optional(), limit: z.number().min(1).max(100).default(20) }))
.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' },
});
const nextCursor = posts.length > input.limit ? posts.pop()!.id : undefined;
return { posts, nextCursor };
}),
create: protectedProcedure
.input(z.object({ title: z.string().min(1).max(255), 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?.authorId !== ctx.session.user.id)
throw new TRPCError({ code: 'FORBIDDEN' });
return ctx.db.post.delete({ where: { id: input.id } });
}),
});
// server/trpc/root.ts
export const appRouter = createTRPCRouter({ post: postRouter });
export type AppRouter = typeof appRouter;
// app/api/trpc/[trpc]/route.ts
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
import { appRouter } from '@/server/trpc/root';
import { createTRPCContext } from '@/server/trpc/trpc';
const handler = (req: Request) =>
fetchRequestHandler({
endpoint: '/api/trpc',
req,
router: appRouter,
createContext: () => createTRPCContext({ headers: req.headers }),
});
export { handler as GET, handler as POST };
// lib/trpc/client.ts
import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from '@/server/trpc/root';
export const api = createTRPCReact<AppRouter>();
// lib/trpc/server.ts — Server Component caller
import { createCallerFactory } from '@trpc/server';
import { appRouter } from '@/server/trpc/root';
const createCaller = createCallerFactory(appRouter);
export const api = createCaller(await createTRPCContext({ headers: new Headers() }));
'use client';
import { api } from '@/lib/trpc/client';
// Query
const { data, isLoading } = api.post.getAll.useQuery({ limit: 10 });
// Infinite query
const { data, fetchNextPage } = api.post.getAll.useInfiniteQuery(
{ limit: 10 },
{ getNextPageParam: (last) => last.nextCursor }
);
// Mutation with optimistic update
const utils = api.useUtils();
const create = api.post.create.useMutation({
onSuccess: () => utils.post.getAll.invalidate(),
});
create.mutate({ title: 'Hello', body: 'World' });
// app/posts/page.tsx — no useQuery, direct caller
import { api } from '@/lib/trpc/server';
export default async function PostsPage() {
const { posts } = await api.post.getAll({ limit: 20 });
return <PostList posts={posts} />;
}
| Pattern | Code |
|---|---|
| Public procedure | publicProcedure.query/mutation |
| Auth-protected | protectedProcedure (throws UNAUTHORIZED) |
| Input validation | .input(z.object({...})) — always use Zod |
| Cursor pagination | take: limit+1, return nextCursor |
| Invalidate cache | utils.router.procedure.invalidate() |
| Server Component | createCallerFactory + direct call |
❌ Skipping input validation on procedures
✅ Always .input(z.schema()) — tRPC validates automatically
❌ One giant router file
✅ Split by domain: postRouter, userRouter, merged in root
❌ Fetching in Server Component via HTTP (localhost)
✅ Use createCallerFactory for direct in-process calls
❌ Not using superjson transformer
✅ superjson handles Date, Map, Set serialization automatically