tRPC هي طبقة واجهة برمجة التطبيقات (API) الآمنة من النوع الشامل والتي تلغي الحاجة إلى عقود واجهة برمجة التطبيقات (API)، وإنشاء التعليمات البرمجية، وتكرار الكتابة بين الواجهة الخلفية والواجهة الأمامية لـ TypeScript. في عام 2026، أصبح الإصدار 11 من tRPC مع تكامل React Query ودعم Next.js App Router هو المعيار لتطبيقات TypeScript الكاملة. يغطي هذا الدليل كل شيء بدءًا من الإعداد وحتى أنماط الإنتاج.
📋 Table of Contents
لماذا tRPC؟
- عقد صفر API— أنواع الواجهة الخلفية متاحة تلقائيًا على الواجهة الأمامية
- لا يوجد توليد التعليمات البرمجية– الأنواع المستنتجة في وقت الترجمة، ولم يتم إنشاؤها
- تايب سكريبت الكامل– الإكمال التلقائي لاستدعاءات API والوسائط وأنواع الإرجاع
- رد الفعل الاستعلام المدمج في— التخزين المؤقت، وتحميل الحالات، وإعادة جلب العمل تلقائيًا
- نفس DX مثل استدعاء الوظيفة —
trpc.user.getById.useQuery(1)
الإعداد باستخدام جهاز توجيه تطبيق Next.js
npm install @trpc/server @trpc/client @trpc/react-query @trpc/next
npm install @tanstack/react-query
npm install zod
src/
server/
api/
trpc.ts — tRPC base config
root.ts — root router
routers/
user.ts
post.ts
trpc/
react.tsx — client setup
server.ts — server-side caller
إعداد الخادم
// server/api/trpc.ts
import { initTRPC, TRPCError } from '@trpc/server';
import { ZodError } from 'zod';
// Context type — available in all procedures
export interface Context {
userId: string | null;
db: PrismaClient;
}
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;
// Auth middleware
const isAuthenticated = t.middleware(({ ctx, next }) => {
if (!ctx.userId) {
throw new TRPCError({ code: 'UNAUTHORIZED' });
}
return next({ ctx: { ...ctx, userId: ctx.userId } });
});
export const protectedProcedure = t.procedure.use(isAuthenticated);
// Rate limiting middleware
const withRateLimit = t.middleware(async ({ ctx, next }) => {
const key = `ratelimit:${ctx.userId ?? 'anonymous'}`;
const count = await redis.incr(key);
if (count === 1) await redis.expire(key, 60);
if (count > 100) throw new TRPCError({ code: 'TOO_MANY_REQUESTS' });
return next();
});
export const rateLimitedProcedure = protectedProcedure.use(withRateLimit);
أجهزة التوجيه
// server/api/routers/user.ts
import { z } from 'zod';
import { router, publicProcedure, protectedProcedure } from '../trpc';
export const userRouter = router({
// Query — get data
getById: publicProcedure
.input(z.number().int().positive())
.query(async ({ ctx, input }) => {
const user = await ctx.db.user.findUnique({ where: { id: input } });
if (!user) throw new TRPCError({ code: 'NOT_FOUND', message: `User ${input} not found` });
return user;
}),
// Query with pagination
list: publicProcedure
.input(z.object({
page: z.number().default(1),
limit: z.number().max(100).default(20),
search: z.string().optional(),
}))
.query(async ({ ctx, input }) => {
const { page, limit, search } = input;
const [users, total] = await ctx.db.$transaction([
ctx.db.user.findMany({
where: search ? { name: { contains: search, mode: 'insensitive' } } : undefined,
skip: (page - 1) * limit,
take: limit,
}),
ctx.db.user.count(),
]);
return { users, total, page, limit };
}),
// Mutation — create/update/delete
create: protectedProcedure
.input(z.object({
name: z.string().min(2).max(50),
email: z.string().email(),
}))
.mutation(async ({ ctx, input }) => {
return ctx.db.user.create({ data: input });
}),
update: protectedProcedure
.input(z.object({
id: z.number(),
name: z.string().optional(),
email: z.string().email().optional(),
}))
.mutation(async ({ ctx, input }) => {
const { id, ...data } = input;
return ctx.db.user.update({ where: { id }, data });
}),
// Subscription — real-time
onUserCreated: protectedProcedure.subscription(({ ctx }) => {
return observable<User>((emit) => {
const unsub = eventEmitter.on('user.created', (user) => {
emit.next(user);
});
return () => unsub();
});
}),
});
جهاز توجيه الجذر
// server/api/root.ts
import { router } from './trpc';
import { userRouter } from './routers/user';
import { postRouter } from './routers/post';
export const appRouter = router({
user: userRouter,
post: postRouter,
});
export type AppRouter = typeof appRouter;
معالج جهاز توجيه التطبيقات Next.js
// app/api/trpc/[trpc]/route.ts
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
import { appRouter } from '@/server/api/root';
import { createContext } from '@/server/api/context';
const handler = (req: Request) =>
fetchRequestHandler({
endpoint: '/api/trpc',
req,
router: appRouter,
createContext,
});
export { handler as GET, handler as POST };
استخدام العميل في React
// trpc/react.tsx
'use client';
import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from '@/server/api/root';
export const trpc = createTRPCReact<AppRouter>();
// components/UserList.tsx
'use client';
import { trpc } from '@/trpc/react';
export function UserList() {
const { data, isLoading, error } = trpc.user.list.useQuery({
page: 1,
limit: 20,
search: 'alice',
});
const createUser = trpc.user.create.useMutation({
onSuccess: () => {
utils.user.list.invalidate(); // refetch list
},
});
const utils = trpc.useUtils();
if (isLoading) return <Spinner />;
if (error) return <Error message={error.message} />;
return (
<div>
{data?.users.map(user => <UserCard key={user.id} user={user} />)}
<button onClick={() => createUser.mutate({ name: 'Bob', email: 'bob@example.com' })}>
{createUser.isPending ? 'Creating...' : 'Add User'}
</button>
</div>
);
}
// Server component usage (no hook)
import { api } from '@/trpc/server';
export default async function Page() {
const users = await api.user.list.query({ page: 1, limit: 10 });
return <UserList initialData={users} />;
}
يعد tRPC في عام 2026 هو المعيار الذهبي لسلامة نوع TypeScript الكامل. يعد تحسين DX مثيرًا – يقوم IDE الخاص بك بإكمال وسيطات واجهة برمجة التطبيقات (API) تلقائيًا وإرجاع الأنواع دون أي خطوة لإنشاء التعليمات البرمجية. بالاشتراك مع Zod للتحقق من صحة الإدخال وNext.js App Router، ينشئ tRPC أضيق تجربة ممكنة لـ TypeScript كاملة المكدس. استخدمه لأي مشروع حيث يتم استخدام TypeScript على كل من العميل والخادم.
🔗 Share this article
✍️ Leave a Comment