Mundarija (24)
- Bu darsda
- 1. Nega bu kerak?
- 2. Server: router va procedure
- 2.1 initTRPC va context
- 2.2 Middleware va himoyalangan procedure
- 2.3 Router
- 3. Klient: tiplar qayerdan keladi
- 3.1 Tipli chaqiruv
- 3.2 TypeScript nimani ushlaydi
- 4. HTTP qatlami: batch, xato kodlari
- 4.1 httpBatchLink
- 4.2 Xato kodlari
- 5. Ichkaridan: procedure zanjiri
- 6. Qachon tRPC, qachon emas
- 7. Hujumchi nigohi
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): bo'shliqlarni to'ldiring
- 2-mashq (o'rta): rol middleware'i
- 3-mashq (qiyin): batch link
- 10. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
- 11. Real ishda
- Xulosa
- Manbalar
tRPC: TypeScript monorepo uchun sxemasiz, to'liq tipli API
Qisqacha: tRPC — frontend va backend bitta TypeScript kod bazasida bo'lganda, API'ni alohida sxema yoki kod generatsiyasisiz tipli qiladi. Server router ichida procedure lar e'lon qiladi (
query— o'qish,mutation— o'zgartirish), kirishni Zod bilan tekshiradi. Klient serverning faqat tipini (typeof appRouter) import qiladi — vaapi.vazifa.royxat.query(...)chaqiruvining argumenti ham, natijasi ham kompilyatsiya paytida tekshiriladi. Ommaviy API, mobil yoki boshqa tildagi mijoz uchun esa REST/OpenAPI to'g'riroq.
Bu darsda
- tRPC router,
queryvamutationprocedure'larini Zod input bilan yozasiz. contextva middleware bilan himoyalangan procedure yasaysiz.- Klient tiplari qayerdan kelishini va TypeScript qanday xatolarni ushlashini o'z ko'zingiz bilan ko'rasiz.
httpBatchLink, xato kodlari va ularning HTTP statuslarini bilasiz.- tRPC, REST + OpenAPI va GraphQL orasida tanlay olasiz.
Oldin bilishingiz kerak: Workspaces va monorepo asoslari, Zod 4 serverda, Code-first OpenAPI va klient generatsiyasi, GraphQL asoslari.
1. Nega bu kerak?
Oldingi uch darsda frontend va backend o'rtasida "shartnoma" yasadik: OpenAPI hujjati, GraphQL sxemasi. Ikkalasida ham zanjir bor: sxema → generator → tiplar → frontend. Generatsiyani unutsangiz — tiplar eskiradi.
Endi boshqa vaziyatni tasavvur qiling: vazifalar frontendi (Next.js) va backend bitta monorepo'da (Workspaces va monorepo asoslari), ikkalasi TypeScript, bitta jamoa. Bu yerda sxema kimga kerak? TypeScript'ning o'zi backend funksiyasining tipini biladi. Faqat HTTP chegarasidan "o'tkazib" berish kerak.
RPC (Remote Procedure Call — masofadagi funksiyani chaqirish) — boshqa kompyuterdagi funksiyani o'zingizniki kabi chaqirish g'oyasi: vazifaYarat(...) yozasiz, aslida esa tarmoq orqali so'rov ketadi. tRPC (TypeScript RPC) shuni TypeScript uchun qiladi. Server — oddiy funksiyalar. Klient — proksi (vakil obyekt): siz uning metodini chaqirasiz, u esa buni HTTP so'rovga aylantirib serverga yuboradi. Tiplar import type bilan to'g'ridan oqadi: generator yo'q, YAML yo'q, sxema fayli yo'q.
Klientdagi chaqiruv tarmoqda nimaga aylanadi:
| Klientda | HTTP | Serverda |
|---|---|---|
api.vazifa.royxat.query(...) |
GET, URL'da vazifa.royxat |
royxat procedure'i |
api.vazifa.yarat.mutate(...) |
POST, tanada JSON | yarat procedure'i |
| natija | JSON javob | procedure qaytargan qiymat |
O'xshatish: restorandagi ofitsiant. Siz menyudagi taom nomini aytasiz (vazifa.royxat), ofitsiant oshxonaga borib, tayyor taomni olib keladi. Oshxona qanday ishlashini bilishingiz shart emas — menyu (tip) yetarli.
Kanon tajribasi (2026-10-27): tRPC 11.19.0, zod 4.6.7, Node 24.21.0 — router, Zod input, auth middleware, batch link. Kanon tiplarni .mjs faylda tekshira olmagan — shu sababli TypeScript xatolarini biz alohida ishga tushirdik (typescript 6.0.3, tsc --noEmit, 2026-10-28).
2. Server: router va procedure
2.1 initTRPC va context
tRPC ilovasi bitta t obyektidan boshlanadi. Unga context tipi beriladi — har so'rov uchun yaratiladigan obyekt (GraphQL'dagi context bilan bir xil g'oya):
import { initTRPC, TRPCError } from "@trpc/server";
import { z } from "zod";
type Kontekst = { foydalanuvchiId: number | null };
const t = initTRPC.context<Kontekst>().create();foydalanuvchiId — 28-qismdagi sessiyadan keladi. Bu yerda faqat tip.
2.2 Middleware va himoyalangan procedure
const himoyalangan = t.procedure.use(({ ctx, next }) => {
if (ctx.foydalanuvchiId === null) {
throw new TRPCError({ code: "UNAUTHORIZED" });
}
return next({ ctx: { foydalanuvchiId: ctx.foydalanuvchiId } });
});Ikki narsa bor. Birinchisi — middleware xato tashlasa, procedure chaqirilmaydi (Express middleware zanjiri g'oyasi, Middleware va request lifecycle). Ikkinchisi muhimroq: next({ ctx: ... }) context'ni toraytiradi. Middleware'dan keyin ctx.foydalanuvchiId tipi number | null emas, number. TypeScript buni biladi — procedure ichida null tekshiruvi kerak emas.
2.3 Router
export const appRouter = t.router({
vazifa: t.router({
royxat: himoyalangan
.input(z.object({ bajarildi: z.boolean().optional() }))
.query(({ input, ctx }) =>
vazifalar.filter(
(v) =>
v.egasiId === ctx.foydalanuvchiId &&
(input.bajarildi === undefined ||
v.bajarildi === input.bajarildi),
),
),
yarat: himoyalangan
.input(z.object({ matn: z.string().trim().min(1).max(100) }))
.mutation(({ input, ctx }) => ({
id: 3,
matn: input.matn,
egasiId: ctx.foydalanuvchiId,
})),
}),
});
export type AppRouter = typeof appRouter;.input(zod)— kirish sxemasi. tRPC kelgan ma'lumotniparseqiladi; xato bo'lsa —BAD_REQUEST, procedure chaqirilmaydi. Procedure ichidainputtipi Zod'dan chiqariladi..query()— o'qish (HTTP GET),.mutation()— o'zgartirish (HTTP POST).- Router'lar ichma-ich:
vazifa.royxat,vazifa.yarat. Klientda ham shu yo'l. export type AppRouter— eng muhim qator. Klient faqat shu tipni oladi, server kodini emas.
Express'ga ulash — adapter orqali (hujjat bo'yicha): app.use("/trpc", createExpressMiddleware({ router: appRouter, createContext })). createContext({ req }) — so'rovdan sessiyani o'qib, Kontekst qaytaradi.
3. Klient: tiplar qayerdan keladi
3.1 Tipli chaqiruv
import { createTRPCClient, httpBatchLink } from "@trpc/client";
import type { AppRouter } from "./server.ts";
const api = createTRPCClient<AppRouter>({
links: [httpBatchLink({ url: "http://127.0.0.1:4000/trpc" })],
});
const royxat = await api.vazifa.royxat.query({ bajarildi: false });
// royxat tipi:
// { id: number; matn: string; bajarildi: boolean;
// egasiId: number }[]import type — kompilyatsiyadan keyin iz qolmaydi: server kodi (baza ulanishi, sirlar) frontend bundle'ga tushmaydi. royxat tipini hech kim yozmadi — TypeScript uni server procedure'ining qaytarish qiymatidan chiqardi.
flowchart LR
S["server.ts: appRouter"] --> T["type AppRouter"]
T -. import type .-> K["klient.ts: createTRPCClient"]
K -- HTTP JSON --> SDiagrammaga qarang: tip faqat kompilyatsiya paytida oqadi (punktir chiziq), ishga tushishda esa oddiy HTTP so'rov. Kompilyatsiyadan keyin klientda server kodidan hech narsa qolmaydi.
3.2 TypeScript nimani ushlaydi
Kanon buni sinay olmagan (.mjs). Biz serverni server.ts ga, klientni klient.ts ga yozib, ataylab to'rtta xato qildik:
const royxat = await api.vazifa.royxat.query({ bajarildi: "ha" });
const birinchi = royxat[0];
console.log(birinchi?.matnn);
await api.vazifa.yarat.mutate({ matn: 42 });
await api.vazifa.ochir.mutate({ id: 1 });tsc --noEmit natijasi (typescript 6.0.3, @trpc/server 11.19.0; oxirgi xabar uzun — ichki tip nomlarini … bilan qisqartirdik):
klient.ts(8,48): error TS2322: Type 'string' is not assignable to type 'boolean | undefined'.
klient.ts(10,23): error TS2551: Property 'matnn' does not exist on type '{ id: number; matn: string; bajarildi: boolean; egasiId: number; }'. Did you mean 'matn'?
klient.ts(11,33): error TS2322: Type 'number' is not assignable to type 'string'.
klient.ts(12,18): error TS2339: Property 'ochir' does not exist on type 'DecoratedProcedureRecord<…>'.To'rttala xato — brauzerga yetmasdan, kod yozish paytida (muharrir ham qizil chiziq bilan ko'rsatadi):
bajarildi: "ha"— Zod sxemasidagiz.boolean().optional()→boolean | undefined.matnn— server qaytargan obyektda bunday maydon yo'q, TypeScript hattomatnni taklif qildi.matn: 42— kirish sxemasistring.ochir— router'da bunday procedure yo'q.
Endi backend'da matn ni sarlavha ga o'zgartiring — frontend'dagi har murojaat darhol xato beradi. "Rename symbol" (F2) esa ikkala tomonda birga o'zgartiradi. Bu tRPC'ning asosiy qiymati: bitta refaktoring — butun stek.
Tekshirib ko'ring: TypeScript xatolari ishga tushishdagi tekshiruvni almashtiradimi? Ya'ni
.input(zod)ni olib tashlasa bo'ladimi, axir klient baribir tipli?
Javob
Yo'q. Tiplar faqat sizning klientingizni tekshiradi, kompilyatsiya paytida. HTTP endpoint esa ochiq: hujumchi curl bilan istalgan JSON yuboradi, eski versiyali ilova esa eski shaklni. Kiruvchi ma'lumot har doim ishonchsiz (Validatsiya). .input(zod) — runtime himoya, tiplar esa uning yon mahsuloti.
4. HTTP qatlami: batch, xato kodlari
4.1 httpBatchLink
Link — klient so'rovni qanday yuborishini belgilaydi. httpBatchLink bir tikdagi chaqiruvlarni bitta HTTP so'rovga yig'adi — oldingi darsdagi DataLoader g'oyasi, tarmoq darajasida. Kanon ikkita parallel chaqiruv yubordi va fetch sonini sanadi. Qator boshiga qarang — chaqiruvlar va HTTP so'rovlar soni:
2 chaqiruv → 2 HTTP so'rov (batch? query va mutation alohida batchlanadi); royxat 3 ta, yarat {"id":1000,"matn":"Non olish","egasiId":2}Kutilmagan natija: 2 ta chaqiruv — 2 ta HTTP so'rov. Sabab: biri query (GET), ikkinchisi mutation (POST) — tRPC ularni bitta to'plamga qo'shmaydi. Ikkita query bilan biz alohida sinab ko'rdik (a va b procedure'lari, standalone adapter):
[ 1, 2 ] [ 'GET /a,b?batch=1&input=%7B%7D' ]Ikki query — bitta GET, procedure nomlari URL'da vergul bilan. Kanon matn: " Non olish " yuborgan, javobda "Non olish" — Zod'dagi .trim() serverda ishladi.
4.2 Xato kodlari
Kanon ikki xatoni ataylab chaqirdi: bo'sh matn va sessiyasiz so'rov. Har qatordagi xato kodiga qarang (birinchi qator kanon yozuvida kesilgan):
zod xatosi: BAD_REQUEST [ { "origin": "string", "code": "too_small", "minimum": 1, "in
auth'siz: UNAUTHORIZED 401Nima bo'ldi? Bo'sh matn Zod tekshiruvidan o'tmadi — procedure chaqirilmadi, BAD_REQUEST qaytdi. Sessiyasiz so'rov esa middleware'da to'xtadi va haqiqiy HTTP 401 oldi.
TRPCError kodlari HTTP statuslarga mos keladi:
| tRPC kodi | HTTP | Qachon |
|---|---|---|
BAD_REQUEST |
400 | Zod input xatosi |
UNAUTHORIZED |
401 | kirilmagan |
FORBIDDEN |
403 | ruxsat yo'q |
NOT_FOUND |
404 | topilmadi |
TOO_MANY_REQUESTS |
429 | rate limit |
INTERNAL_SERVER_ERROR |
500 | kutilmagan xato |
GraphQL'dan farqli, tRPC haqiqiy HTTP statuslarni qaytaradi — monitoring va proksi uni to'g'ri ko'radi. Zod xatosi xabari esa — Zod issue'lari JSON matni. Frontend uni chiroyli ko'rsatishi uchun server errorFormatter bilan zodError.flatten() kabi qulay shaklni qo'shadi (hujjat bo'yicha).
5. Ichkaridan: procedure zanjiri
tRPC'ning t.procedure.use(...).input(...).query(...) zanjiri — builder naqshi: har qadam yangi obyekt qaytaradi, oxirgisi funksiyani yig'adi. Mantiqni (tiplarsiz) JS'da takrorlaymiz:
function procedure(qadamlar = []) {
return {
use: (mw) => procedure([...qadamlar, { mw }]),
input: (tekshir) => procedure([...qadamlar, { tekshir }]),
query: (fn) => async (ctx, xom) => {
let input = xom;
for (const q of qadamlar) {
if (q.mw) ctx = q.mw(ctx);
if (q.tekshir) input = q.tekshir(input);
}
return fn({ ctx, input });
},
};
}
class XatoT extends Error {
constructor(code) {
super(code);
this.code = code;
}
}
const himoya = (ctx) => {
if (ctx.fId === null) throw new XatoT("UNAUTHORIZED");
return { fId: ctx.fId };
};
const matnli = (x) => {
const matn = typeof x?.matn === "string" ? x.matn.trim() : "";
if (matn.length === 0) throw new XatoT("BAD_REQUEST");
return { matn };
};
const yarat = procedure()
.use(himoya)
.input(matnli)
.query(({ ctx, input }) => ({ egasi: ctx.fId, matn: input.matn }));
for (const [ctx, kirish] of [
[{ fId: 2 }, { matn: " Non olish " }],
[{ fId: null }, { matn: "Non" }],
[{ fId: 2 }, { matn: " " }],
]) {
try {
console.log(await yarat(ctx, kirish));
} catch (e) {
console.log("xato:", e.code);
}
}Konsolda:
{ egasi: 2, matn: 'Non olish' }
xato: UNAUTHORIZED
xato: BAD_REQUESTTartib muhim: use(himoya) input dan oldin — kirilmagan foydalanuvchi validatsiya xatosini emas, UNAUTHORIZED ni ko'radi. Haqiqiy tRPC'da ham qadamlar aynan e'lon qilingan tartibda bajariladi. Bu yerda TypeScript yo'q — tRPC'ning sehri esa aynan har qadamda tipni ham o'zgartirishda (ctx.fId endi number). Uni JS'da ko'rsatib bo'lmaydi.
6. Qachon tRPC, qachon emas
| Vaziyat | tRPC | REST + OpenAPI | GraphQL |
|---|---|---|---|
| TS monorepo, bitta jamoa | eng qulay | yaxshi | ortiqcha |
| Mobil (Kotlin/Swift) mijoz | yo'q | ha | ha |
| Ommaviy API, hamkorlar | yo'q | ha | ba'zan |
| Ko'p xil mijoz, grafga o'xshash ma'lumot | qiyin | o'rtacha | eng yaxshi |
| HTTP kesh, CDN | GET query'lar bilan | oson | qiyin |
tRPC'ning chegarasi aniq: tiplar faqat TypeScript kompilyatori ikkala tomonni bir vaqtda ko'rganda ishlaydi. Boshqa repo'dagi frontend — tip paketini nashr qilish kerak; boshqa tildagi mijoz — tiplar umuman yo'q, protokol esa tRPC'ning o'ziga xos JSON formati. Kanon xulosasi ham shu: "ochiq API (mobil, uchinchi tomon) uchun REST/OpenAPI".
Yana bir narsa: tRPC procedure'lari URL'da vazifa.royxat — bu RPC, REST emas. Resurslar, metodlar semantikasi, ETag — bularning hammasi chetda qoladi. Ichki API uchun bu tezlik, tashqi API uchun kamchilik. Ikkalasi kerak bo'lsa — trpc-to-openapi kabi paketlar procedure'larni REST sifatida ham ochadi (sinalmagan).
7. Hujumchi nigohi
- "Tip bor — tekshiruv shart emas". Eng xavfli xato:
.input()siz procedure. Endpoint ochiq — hujumchimatn: { "$ne": "" }yoki 10 MB satr yuboradi. Himoya: har procedure'da.input(zod)va Zod'da chegaralar (max(100)). - Himoyalanmagan procedure.
t.procedure(ochiq) vahimoyalanganyonma-yon — bittasini adashtirish oson. Himoya: sukut bo'yicha himoyalangan procedure'dan foydalanish, ochiqlarini alohida nom bilan (ochiqProcedure) va review'da tekshirish. - Resurs egaligi.
vazifa.olish({ id: 8 })— middleware faqat "kirganmi"ni tekshirdi, "bu vazifa unikimi"ni emas. Himoya: procedure ichidaegasiId === ctx.foydalanuvchiId(28-qism, resurs egaligi). - Batch orqali ko'paytirish. Bitta HTTP so'rovda 500 ta procedure — rate limit'ni chetlab o'tadi. Himoya: kerak bo'lmasa serverda batch'ni o'chirish (
allowBatching: false— biz sinadik, batch so'rovga 400 qaytdi) yoki rate limit'ni procedure soni bo'yicha hisoblash. - CSRF. Cookie bilan autentifikatsiya va
mutation— POST; boshqa saytdan forma yuborilishi mumkin. tRPCContent-Type: application/jsontalab qiladi, oddiy forma bunday so'rov yubora olmaydi — lekin bu CORS va SameSite cookie sozlamalariga bog'liq (28-qism).
8. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
Klientda tiplar any |
createTRPCClient ga <AppRouter> berilmagan |
generik parametr |
| Frontend bundle'ga server kodi tushdi | import (tip emas) ishlatilgan |
import type |
BAD_REQUEST, sabab tushunarsiz |
Zod xatosi JSON matn | errorFormatter bilan formatlash |
| 2 chaqiruv — 2 so'rov, batch "ishlamadi" | query va mutation alohida to'planadi | kutilgan xatti-harakat |
| Boshqa repo'dagi ilova tiplarni ko'rmaydi | tRPC — monorepo vositasi | tip paketi yoki OpenAPI |
ctx.foydalanuvchiId hamon number | null |
middleware next() ga yangi ctx bermagan |
next({ ctx: { ... } }) |
9. Mashqlar
Mashqlar kurs/mashqlar/29/61-trpc/ da.
1-mashq (oson): bo'shliqlarni to'ldiring
- O'qish procedure'i — , o'zgartirish — mutation
- Klient serverdan import qiladigan narsa —
AppRouter - Zod input xatosining tRPC kodi —
- Kirilmagan foydalanuvchi uchun HTTP status —
- Bir tikdagi chaqiruvlarni yig'adigan link —
http
Yechim
- query — HTTP GET; mutation — POST.
- tipi —
import type { AppRouter }, server kodi emas. - BAD_REQUEST — HTTP 400.
- 401 —
UNAUTHORIZED. - BatchLink —
httpBatchLink; query va mutation alohida to'planadi.
2-mashq (o'rta): rol middleware'i
Darsdagi procedure builder'ini oling. Ikkinchi middleware yozing — admin: ctx.rol !== "admin" bo'lsa FORBIDDEN. ochir procedure'ini .use(himoya).use(admin).input(...) zanjiri bilan yasang (input: musbat butun id, aks holda BAD_REQUEST). Uch holatni sinang: admin, oddiy foydalanuvchi, kirmagan.
Yechim
function procedure(qadamlar = []) {
return {
use: (mw) => procedure([...qadamlar, { mw }]),
input: (tekshir) => procedure([...qadamlar, { tekshir }]),
mutation: (fn) => async (ctx, xom) => {
let input = xom;
for (const q of qadamlar) {
if (q.mw) ctx = q.mw(ctx);
if (q.tekshir) input = q.tekshir(input);
}
return fn({ ctx, input });
},
};
}
class XatoT extends Error {
constructor(code) {
super(code);
this.code = code;
}
}
const himoya = (ctx) => {
if (ctx.fId === null) throw new XatoT("UNAUTHORIZED");
return ctx;
};
const admin = (ctx) => {
if (ctx.rol !== "admin") throw new XatoT("FORBIDDEN");
return ctx;
};
const idli = (x) => {
if (!Number.isSafeInteger(x?.id) || x.id <= 0) {
throw new XatoT("BAD_REQUEST");
}
return { id: x.id };
};
const ochir = procedure()
.use(himoya)
.use(admin)
.input(idli)
.mutation(({ input }) => ({ ochirildi: input.id }));
const holatlar = [
[{ fId: 1, rol: "admin" }, { id: 7 }],
[{ fId: 2, rol: "foydalanuvchi" }, { id: 7 }],
[{ fId: null, rol: null }, { id: 7 }],
[{ fId: 1, rol: "admin" }, { id: -3 }],
];
for (const [ctx, kirish] of holatlar) {
try {
console.log(await ochir(ctx, kirish));
} catch (e) {
console.log("xato:", e.code);
}
}Konsolda:
{ ochirildi: 7 }
xato: FORBIDDEN
xato: UNAUTHORIZED
xato: BAD_REQUESTKirmagan foydalanuvchi UNAUTHORIZED oldi, FORBIDDEN emas — chunki himoya birinchi. 401 va 403 farqi: "kim ekaningni bilmayman" va "bilaman, lekin ruxsat yo'q". Rollar va ruxsatlarning to'liq modeli (RBAC) — 28-qismda.
3-mashq (qiyin): batch link
batchKlient(yuborish) yozing: u chaqir(nom, input) funksiyasini qaytarsin. Bir tikdagi barcha chaqiruvlar bitta yuborish([{ nom, input }, ...]) ga yig'ilsin (queueMicrotask), lekin nom .yarat, .ochir yoki .yangila bilan tugasa (mutation) — ular query'lardan alohida to'plamda ketsin, xuddi tRPC'dagidek. yuborish har to'plamni chiqarsin va natijalar massivini qaytarsin.
Yechim
const MUTATSIYA = /\.(yarat|ochir|yangila)$/;
function batchKlient(yuborish) {
const navbat = { query: [], mutation: [] };
let rejalangan = false;
async function yubor(tur) {
const toplam = navbat[tur];
navbat[tur] = [];
if (toplam.length === 0) return;
const natijalar = await yuborish(
tur,
toplam.map(({ nom, input }) => ({ nom, input })),
);
toplam.forEach((x, i) => x.ok(natijalar[i]));
}
return function chaqir(nom, input) {
const tur = MUTATSIYA.test(nom) ? "mutation" : "query";
return new Promise((ok) => {
navbat[tur].push({ nom, input, ok });
if (!rejalangan) {
rejalangan = true;
queueMicrotask(() => {
rejalangan = false;
yubor("query");
yubor("mutation");
});
}
});
};
}
let soni = 0;
async function yuborish(tur, toplam) {
soni++;
const nomlar = toplam.map((x) => x.nom).join(",");
const metod = tur === "query" ? "GET" : "POST";
console.log(`HTTP #${soni} ${metod} ${nomlar}`);
return toplam.map((x) => `${x.nom} ok`);
}
const chaqir = batchKlient(yuborish);
const natijalar = await Promise.all([
chaqir("vazifa.royxat", {}),
chaqir("vazifa.yarat", { matn: "Non" }),
chaqir("foydalanuvchi.men", {}),
]);
console.log(natijalar);Konsolda:
HTTP #1 GET vazifa.royxat,foydalanuvchi.men
HTTP #2 POST vazifa.yarat
[ 'vazifa.royxat ok', 'vazifa.yarat ok', 'foydalanuvchi.men ok' ]Uch chaqiruv — ikki HTTP so'rov: ikkala query bitta GET'da, mutation alohida POST'da. Kanon ko'rgan "2 chaqiruv → 2 so'rov" ham shu qoidaning natijasi edi. Haqiqiy httpBatchLink yana URL uzunligi chegarasini (maxURLLength) hisobga oladi: GET'ga sig'maydigan katta to'plamni bo'ladi.
10. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
Bu darsda vazifalar-api ga kod qo'shilmaydi. vazifalar-api — alohida Express REST xizmati, frontend esa alohida loyiha: tRPC'ning asosiy sharti (bitta TS kod bazasi) yo'q. Kanon tRPC'ni tajribada sinadi: Zod input, auth middleware (UNAUTHORIZED 401), trim(), batch'dagi query/mutation ajralishi. Tiplarni esa biz tsc bilan tekshirdik. tRPC odatda Next.js loyihasi ichida (create-t3-app stekida) uchraydi — kursda 23-qismdagi Server Actions bilan bir xil joyni egallaydi.
O'zingiz sinab ko'ring: alohida papkada npm i @trpc/server@11.19.0 @trpc/client@11.19.0 zod@4.6.7 typescript@6.0.3, darsdagi server.ts va klient.ts ni yozing. npx tsc --noEmit bilan to'rtta xatoni o'zingiz ko'ring. Keyin serverda matn ni sarlavha ga o'zgartirib, klientda nechta joy qizarishini sanang.
11. Real ishda
- T3 stek (Next.js + tRPC + Prisma + Tailwind) — kichik va o'rta SaaS loyihalarda ommabop.
- Monorepo'dagi admin panel va ichki asboblar — tezkor ishlab chiqish, sxema yozmasdan.
- React Native (Expo) + bitta TS monorepo — mobil ham tRPC'ni ishlatishi mumkin, agar u o'sha repo'da TypeScript'da bo'lsa.
- Server Actions bilan raqobat — Next.js'da ko'p ishni Server Actions qiladi; tRPC esa freymvorkdan mustaqil API kerak bo'lganda.
- Intervyuda: "tRPC qanday qilib sxemasiz tip beradi?", "tRPC'ni qachon tanlamaysiz?", "Tiplar bor bo'lsa, validatsiya nega kerak?"
Xulosa
- tRPC: router + procedure (
query/mutation) + Zod input; klientimport type { AppRouter }dan tip oladi. - Middleware
next({ ctx })bilan context'ni toraytiradi — procedure ichida tip aniq. - TypeScript xato argument, yo'q maydon va yo'q procedure'ni kod yozish paytida ushlaydi — biz
tscbilan ko'rdik. - Runtime validatsiya (
.input) baribir shart — endpoint ochiq. httpBatchLinkquery va mutation'ni alohida to'playdi; xatolar haqiqiy HTTP statuslar bilan.- Faqat TS monorepo uchun; mobil, boshqa til va ommaviy API — REST/OpenAPI.
Keyingi dars: gRPC va Protocol Buffers — servislar orasida tez, ikkilik va stream'li aloqa: .proto, unary va stream chaqiruvlar, deadline.
Manbalar
- tRPC hujjati (v11): https://trpc.io/docs
- tRPC — xato kodlari: https://trpc.io/docs/server/error-handling
- tRPC —
httpBatchLink: https://trpc.io/docs/client/links/httpBatchLink - tRPC — middleware va context: https://trpc.io/docs/server/middlewares
- Kanon tajribasi (IlmHamroh, 2026-10-27): tRPC 11.19.0, zod 4.6.7, Node 24.21.0. TypeScript xatolari — biz ishga tushirdik (typescript 6.0.3,
tsc --noEmit, 2026-10-28); JS bloklari va mashq yechimlari ham (Node 24.21.0).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!