Mundarija (25)
- Bu darsda
- 1. Nega bu kerak?
- 2. Sxema: SDL
- 2.1 Tiplar
- 2.2 Kirish nuqtalari: Query va Mutation
- 3. Resolver'lar
- 3.1 Har maydon — funksiya
- 3.2 Bajarilish tartibi
- 4. Server: GraphQL Yoga
- 4.1 Ishga tushirish
- 4.2 So'rovlar
- 5. Xatolar
- 5.1 Xato formati
- 5.2 HTTP status: 200 yoki 400?
- 6. REST yoki GraphQL?
- 7. Hujumchi nigohi
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): bo'shliqlarni to'ldiring
- 2-mashq (o'rta): qancha ortiqcha ma'lumot?
- 3-mashq (qiyin): argumentli maydonlar
- 10. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
- 11. Real ishda
- Xulosa
- Manbalar
GraphQL asoslari: sxema, Query, Mutation va resolver'lar Node.js'da
Qisqacha: GraphQL — mijoz o'ziga kerakli maydonlarni o'zi tanlab so'raydigan API tili. Server bitta endpoint'da (
/graphql) tiplangan sxema e'lon qiladi:type Vazifa { id: Int!, matn: String! }, o'qish uchunQuery, o'zgartirish uchunMutation. Har maydonni resolver funksiyasi hisoblaydi. Javob aynan so'rov shaklida keladi, xatolar esaerrorsmassivida — ko'pincha HTTP 200 bilan.
Bu darsda
- SDL'da tiplar, skalyarlar,
!(majburiy), ro'yxatlar vainputtiplarini yozasiz. QueryvaMutationresolver'larini va maydon resolver'ini (Vazifa.egasi) tushuntirasiz.- GraphQL Yoga serveriga so'rov, o'zgaruvchi va mutation yuborib, javob shaklini o'qiysiz.
- GraphQL xato formatini va HTTP status kodlari bilan bog'liq tuzoqni bilasiz.
- REST va GraphQL orasida loyiha uchun tanlay olasiz.
Oldin bilishingiz kerak: REST prinsiplari va resurslar, Filtrlash, saralash va maydon tanlash, OpenAPI 3.1 spetsifikatsiyasi.
1. Nega bu kerak?
vazifalar mobil ilovasi bosh ekranida vazifalar ro'yxatini ko'rsatadi: faqat matn va egasining ismi. REST'da buning uchun:
GET /v1/vazifalar— har vazifaning hamma maydonlari keladi:id,matn,bajarildi,muddat,ustuvorlik,teglar,yaratilgan... Ekranga ikkitasi kerak. Bu — over-fetching (ortiqcha olish).- Egasining ismi javobda yo'q, faqat
egasiId. Har egasi uchunGET /v1/foydalanuvchilar/:id. Bu — under-fetching (yetarlicha olmaslik): bitta ekran uchun ko'p so'rov.
REST'da buning davosi bor: maydon tanlash (?fields=matn) va ?include=egasi. Lekin har endpoint uchun alohida yoziladi. GraphQL (Facebook, 2012-yilda ichki, 2015-yilda ochiq) shu g'oyani API'ning markaziga qo'ydi: mijoz so'rovda kerakli maydonlarni daraxt shaklida yozadi, server aynan shuni qaytaradi.
{
vazifalar(bajarildi: false) {
matn
egasi { ism }
}
}Bitta so'rov, kerakli maydonlar, ichma-ich bog'langan ma'lumot.
Darsdagi server chiqishlari — biz ishga tushirgan GraphQL Yoga 5.24.6 + graphql 17.0.2 serveridan (Node 24.21.0, 2026-10-28) va kanon tajribasidan (2026-10-27). Apollo Server — sinalmagan, hujjat bo'yicha.
2. Sxema: SDL
2.1 Tiplar
GraphQL API'ning markazi — sxema (schema). U SDL (Schema Definition Language) tilida yoziladi:
type Foydalanuvchi {
id: Int!
ism: String!
}
type Vazifa {
id: Int!
matn: String!
bajarildi: Boolean!
egasi: Foydalanuvchi!
}- Skalyar tiplar —
Int(32 bitli butun),Float,String,Boolean,ID(identifikator, satr sifatida uzatiladi). !— majburiy (non-null).String!— hech qachonnullemas.!siz —nullbo'lishi mumkin. Diqqat: bu TypeScript'ga teskari — GraphQL'da sukut nullable.[Vazifa!]!— ro'yxatning o'zinullemas va ichidagi elementlar hamnullemas.- Obyekt tiplar bir-biriga bog'lanadi:
Vazifa.egasi—Foydalanuvchi. Bu bog'lanish so'rovda ichma-ich tanlashni beradi.
O'z skalyar tiplaringiz ham bo'ladi (scalar DateTime) — ularni serializatsiya qilish qoidasi bilan e'lon qilasiz.
2.2 Kirish nuqtalari: Query va Mutation
Ikkita maxsus tip — API'ning "eshiklari":
type Query {
vazifalar(bajarildi: Boolean): [Vazifa!]!
vazifa(id: Int!): Vazifa
}
input YangiVazifa {
matn: String!
}
type Mutation {
vazifaQosh(kirish: YangiVazifa!): Vazifa!
bajar(id: Int!): Vazifa!
}Query— o'qish (REST'dagiGET). Maydonlari argument olishi mumkin:vazifa(id: Int!).Mutation— o'zgartirish (POST,PATCH,DELETE). Nomi — fe'l:vazifaQosh,bajar.input— argument sifatida beriladigan obyekt. Oddiytypeargument bo'la olmaydi —inputalohida, chunki kirish va chiqish shakli odatda farq qiladi (kirishdaidyo'q).vazifa(id: Int!): Vazifa— natija nullable: topilmasanull. Bu GraphQL'dagi "404".
Uchinchi kirish nuqtasi — Subscription (realtime) — keyingi darsda.
Sxema — shartnoma. U OpenAPI bilan bir xil vazifani bajaradi, faqat API tilining ichiga qurilgan: server sxemani introspection so'rovi bilan o'zi tavsiflaydi, vositalar (GraphiQL, kod generatorlari) uni shu yo'l bilan o'qiydi.
3. Resolver'lar
3.1 Har maydon — funksiya
Sxema "nima bor" deydi. Resolver — "qanday olinadi". Har resolver to'rt argument oladi: (parent, args, context, info).
const resolvers = {
Query: {
vazifalar: (_p, { bajarildi }) =>
bajarildi === undefined
? VAZ
: VAZ.filter((v) => v.bajarildi === bajarildi),
vazifa: (_p, { id }) => VAZ.find((v) => v.id === id) ?? null,
},
Mutation: {
vazifaQosh: (_p, { kirish }) => {
const v = { id: keyingiId++, matn: kirish.matn.trim(),
bajarildi: false, egasiId: 1 };
VAZ.push(v);
return v;
},
},
Vazifa: {
egasi: (v) => EGALAR.find((e) => e.id === v.egasiId),
},
};parent— ota obyekt.Vazifa.egasiresolveri uchun bu — vazifaning o'zi (v), u yerdanegasiIdolinadi.args— maydon argumentlari ({ id: 2 }), sxemaga ko'ra tekshirilgan.context— har so'rov uchun umumiy obyekt: joriy foydalanuvchi, baza ulanishi, loader'lar. Autentifikatsiya shu yerda (28-qism sessiyasicontextga tushadi).info— so'rovning o'zi haqida ma'lumot (qaysi maydonlar tanlangan). Kamdan-kam kerak.
Resolver yozilmagan maydon uchun sukut resolver ishlaydi: parent[maydonNomi]. Shuning uchun Vazifa.matn uchun resolver yozmadik — vazifa obyektida matn bor. egasi esa obyektda yo'q, faqat egasiId — resolver kerak.
3.2 Bajarilish tartibi
Server so'rovni daraxt bo'ylab yuradi: avval Query.vazifalar — massiv. Keyin har element uchun tanlangan maydonlar: matn (sukut), egasi (resolver). egasi natijasi uchun yana tanlanganlar: ism. Mantiqni JS'da takrorlaymiz — kichik so'rov tahlilchisi va bajaruvchi bilan:
function tahlil(matn) {
const tok = matn.match(/[{}]|\w+/g);
let i = 0;
function tanlov() {
const maydonlar = {};
i++; // "{"
while (tok[i] !== "}") {
const nom = tok[i++];
maydonlar[nom] = tok[i] === "{" ? tanlov() : true;
}
i++; // "}"
return maydonlar;
}
return tanlov();
}
function bajar(qiymat, tanlov, resolverlar, tur) {
if (Array.isArray(qiymat)) {
return qiymat.map((x) => bajar(x, tanlov, resolverlar, tur));
}
const natija = {};
for (const [nom, ichki] of Object.entries(tanlov)) {
const r = resolverlar[tur]?.[nom];
const v = r ? r(qiymat) : qiymat[nom];
natija[nom] = ichki === true
? v
: bajar(v, ichki, resolverlar, ichki.__tur ?? nom);
}
return natija;
}
const EGALAR = [{ id: 1, ism: "Aziz" }, { id: 2, ism: "Dilnoza" }];
const VAZ = [
{ id: 1, matn: "Non olish", bajarildi: false, egasiId: 1 },
{ id: 2, matn: "Hisobot", bajarildi: true, egasiId: 2 },
];
const resolverlar = {
Vazifa: { egasi: (v) => EGALAR.find((e) => e.id === v.egasiId) },
};
const sorov = tahlil("{ matn egasi { ism } }");
console.log(JSON.stringify(sorov));
console.log(JSON.stringify(bajar(VAZ, sorov, resolverlar, "Vazifa")));Konsolda:
{"matn":true,"egasi":{"ism":true}}
[{"matn":"Non olish","egasi":{"ism":"Aziz"}},{"matn":"Hisobot","egasi":{"ism":"Dilnoza"}}]Javob aynan so'rov shaklida: bajarildi, id, egasiId so'ralmagan — ular yo'q. Bu o'quv bajaruvchisi: haqiqiy graphql paketi argumentlarni, fragmentlarni, tiplarni tekshirishni, asinxron resolver'larni va xatolarni ham biladi. Bizdagi __tur esa tip nomini topish uchun soddalashtirish — haqiqiy bajaruvchi buni sxemadan biladi.
Bajarish daraxti shunday ko'rinadi:
flowchart TB
Q["Query.vazifalar"] --> V1["Vazifa 1"]
Q --> V2["Vazifa 2"]
V1 --> M1["matn: sukut resolver"]
V1 --> E1["egasi: resolver"]
E1 --> I1["ism: sukut"]
V2 --> M2["matn: sukut resolver"]
V2 --> E2["egasi: resolver"]
E2 --> I2["ism: sukut"]Har egasi tuguni — alohida resolver chaqiruvi. Vazifalar ko'paysa, bu tugunlar ham ko'payadi.
Tekshirib ko'ring: So'rovda
egasi { ism }100 ta vazifa uchun so'ralsa,Vazifa.egasiresolveri necha marta chaqiriladi?
Javob
100 marta — har vazifa uchun alohida. Agar resolver ichida bazaga so'rov bo'lsa — 100 ta SQL so'rov. Bu GraphQL'dagi mashhur N+1 muammosi (N+1 muammosi). Kanon buni o'lchagan: 50 vazifa — 50 "baza" chaqiruvi. Yechim — DataLoader, keyingi darsda.
4. Server: GraphQL Yoga
4.1 Ishga tushirish
Node'da ikki mashhur server bor:
- GraphQL Yoga (The Guild) — Web standart
Request/Responseustiga qurilgan, Nodehttp, Express, Fastify, Bun, Deno, edge'da bir xil ishlaydi. Kanon shuni tanlagan. - Apollo Server — eng ko'p tarqalgan, Apollo ekotizimi (Client, Federation, Studio) bilan. Kanonda sinalmagan.
Ikkalasi ham graphql (graphql-js) paketining ustida — sxema, tahlil va bajarish uniki. Bizning server (resolver'lar yuqorida, typeDefs — darsdagi SDL):
import { createYoga, createSchema } from "graphql-yoga";
import http from "node:http";
const yoga = createYoga({
schema: createSchema({ typeDefs, resolvers }),
graphiql: false,
});
http.createServer(yoga).listen(4000);graphiql: true (sukut) bo'lsa, brauzerda /graphql ni ochganda GraphiQL — so'rov yozish, avtoto'ldirish va hujjat paneli — chiqadi. Production'da odatda o'chiriladi.
4.2 So'rovlar
GraphQL so'rovi — oddiy HTTP POST: tanada JSON { "query": "...", "variables": {...} }. Biz quyidagi so'rovlarni yubordik (har javob oldida HTTP status):
await gql("{ vazifalar(bajarildi: false) { matn egasi { ism } } }");
await gql(
"query Bitta($id: Int!) { vazifa(id: $id) { id matn bajarildi } }",
{ id: 2 },
);
await gql("{ vazifa(id: 99) { matn } }");
await gql(
"mutation { vazifaQosh(kirish: { matn: \" Sut olish \" }) " +
"{ id matn bajarildi } }",
);200 {"data":{"vazifalar":[{"matn":"Non olish","egasi":{"ism":"Aziz"}},{"matn":"Ingliz tili mashqi","egasi":{"ism":"Aziz"}}]}}
200 {"data":{"vazifa":{"id":2,"matn":"Hisobot tayyorlash","bajarildi":true}}}
200 {"data":{"vazifa":null}}
200 {"data":{"vazifaQosh":{"id":4,"matn":"Sut olish","bajarildi":false}}}- Javob doim
dataichida, so'rov shaklida. - Ikkinchi so'rov — o'zgaruvchi bilan:
$idso'rov matnida, qiymati alohidavariablesda. Qiymatni so'rov satriga yopishtirmang — xuddi SQL'dagi parametrlar kabi (Parametrli so'rovlar va SQL injection).Bitta— operatsiya nomi: loglarda va kuzatuvda qaysi so'rov ekanini ko'rsatadi. - Uchinchi: topilmadi — xato emas,
null(sxemadaVazifanullable). - To'rtinchi — mutation. Resolver
trim()qildi.
GET ham mumkin: GET /graphql?query={ vazifa(id: 1) { matn } } — biz yubordik, javob 200 {"data":{"vazifa":{"matn":"Non olish"}}}. GET faqat query uchun. Mutation'ni GET bilan yuborib ko'rdik:
GET mutation 405 {"errors":[{"message":"Can only perform a mutation operation from a POST request.","extensions":{"code":"BAD_REQUEST"}}]}Sabab — o'zgartirish GET bilan bo'lmasligi kerak: havola yoki <img> orqali mutation yuborish (CSRF) yo'li yopiladi. GET so'rovlarni HTTP keshlash mumkin — buni keyingi darsda persisted queries bilan ko'ramiz.
5. Xatolar
5.1 Xato formati
GraphQL'da xatolar errors massivida. Biz uch xil xato yubordik:
200 {"errors":[{"message":"Vazifa topilmadi","locations":[{"line":1,"column":12}],"path":["bajar"],"extensions":{"code":"NOT_FOUND"}}],"data":null}
200 {"errors":[{"message":"Cannot query field \"parol\" on type \"Vazifa\".","locations":[{"line":1,"column":19}],"extensions":{"code":"GRAPHQL_VALIDATION_FAILED"}}]}
200 {"errors":[{"message":"Argument \"Query.vazifa(id:)\" of type \"Int!\" is required, but it was not provided.","locations":[{"line":1,"column":3}],"extensions":{"code":"GRAPHQL_VALIDATION_FAILED"}}]}Ikki xil xato bor:
- Bajarish xatosi (birinchisi) — resolver
GraphQLErrortashladi:throw new GraphQLError("Vazifa topilmadi", { extensions: { code: "NOT_FOUND" } }).path— qaysi maydonda,extensions.code— mashina uchun kod (REST'dagi Problem Detailstypeiga o'xshash).data: null—bajarmaydoniVazifa!(non-null), shuning uchunnullota darajaga ko'tarildi. - Validatsiya xatosi (ikkinchi, uchinchi) — so'rov sxemaga mos emas:
parolmaydoni yo'q; majburiyidberilmagan. Bundadataumuman yo'q: so'rov bajarilmadi.
Validatsiya xatolari tarjima qilinmaydi — ular dasturchi uchun. Foydalanuvchiga ko'rsatiladigan xato — bajarish xatosi, extensions.code bo'yicha frontend tarjima qiladi.
Muhim: bitta so'rovda bir nechta maydon bo'lsa, biri xato bo'lib, qolganlari muvaffaqiyatli bo'lishi mumkin — qisman natija: data da muvaffaqiyatlilar, errors da xatolar. REST'da bunday holat deyarli yo'q.
5.2 HTTP status: 200 yoki 400?
Yuqoridagi uchala xato — HTTP 200. REST'dan kelgan dasturchini bu chalg'itadi: monitoring "hamma so'rov muvaffaqiyatli" deydi. Biz bitta tajriba qildik: o'sha validatsiya xatosini ikki xil Accept sarlavhasi bilan yubordik:
application/json { salom } 200 application/json; charset=utf-8
application/json { parol } 200 application/json; charset=utf-8
application/graphql-response+json { salom } 200 application/graphql-response+json; charset=utf-8
application/graphql-response+json { parol } 400 application/graphql-response+json; charset=utf-8Yangi media tur application/graphql-response+json bilan so'ralsa, Yoga validatsiya xatosiga 400 qaytardi. Bu — "GraphQL over HTTP" spetsifikatsiyasi qoralamasi (GraphQL Foundation) talabi: yangi media turda so'rov bajarilmagan bo'lsa — 4xx; eski application/json da esa moslik uchun 200 qoladi. Yangi klientlar (graphql-http, urql, Apollo Client'ning yangi versiyalari) yangi turni so'raydi. Monitoringda shunga qarab, errors maydonini ham hisobga oling.
6. REST yoki GraphQL?
| REST | GraphQL | |
|---|---|---|
| Endpoint | ko'p (/vazifalar, /vazifalar/:id) |
bitta (/graphql) |
| Javob shakli | server belgilaydi | mijoz tanlaydi |
| Shartnoma | OpenAPI (alohida) | sxema (ichida, introspection) |
| HTTP kesh | oson (GET + ETag) |
qiyin (POST, bitta URL) |
| Xato | status kodlar | errors + ko'pincha 200 |
| Fayl yuklash | oddiy multipart | qo'shimcha spetsifikatsiya |
| Xavf | ma'lum | chuqur so'rovlar, N+1 (keyingi dars) |
GraphQL kuchli joylari: ko'p xil mijoz (veb, mobil, smart-TV) bir xil ma'lumotning turli bo'laklarini so'raydi; ma'lumot grafga o'xshash (foydalanuvchi → buyurtmalar → mahsulotlar → sharhlar); frontend jamoasi backend'ni kutmasdan maydon tanlashni xohlaydi. Zaif joylari: oddiy CRUD, ochiq ommaviy API (keshlash, rate limit oddiy bo'lishi kerak), fayl va strim.
Ko'p kompaniyada ikkalasi birga: ichki mobil/veb uchun GraphQL, hamkorlar uchun REST.
7. Hujumchi nigohi
- Sxemani o'rganadi. Introspection yoqiq bo'lsa, hujumchi butun API'ni — ichki maydonlar va mutation'lar bilan — bitta so'rovda oladi. Himoya: ochiq bo'lmagan API'da production'da introspection'ni o'chirish (yashirish xavfsizlik emas, lekin hujumni qiyinlashtiradi); maxfiy maydonni sxemaga umuman qo'ymaslik.
- Ruxsatsiz maydonni so'raydi. REST'da himoya endpoint darajasida, GraphQL'da esa har maydon darajasida bo'lishi kerak:
Foydalanuvchi.emailni faqat egasi ko'rsin. Himoya: resolver ichidacontext.foydalanuvchini tekshirish yoki ruxsat qatlami (graphql-shield, direktivalar). - Og'ir so'rov yuboradi.
egasi { vazifalar { egasi { vazifalar ... } } }— ichma-ich cheksiz. Himoya — chuqurlik va murakkablik limiti (keyingi dars). - Xato xabaridan o'rganadi. Kutilmagan xatoda stack trace yoki SQL matni
errors[].messagega tushsa — ichki ma'lumot sizadi. Biz resolver ichidanew Error("SELECT * FROM maxfiy jadval xatosi")tashlab ko'rdik:
masking 200 {"errors":[{"message":"Unexpected error.","locations":[{"line":1,"column":9}],"path":["buzuq"],"extensions":{"code":"INTERNAL_SERVER_ERROR"}}],"data":{"salom":"Salom","buzuq":null}}Yoga oddiy Error matnini Unexpected error. bilan almashtirdi (masking) — faqat GraphQLError matni mijozga ko'rinadi. Shu javobda qisman natija ham ko'rinyapti: salom muvaffaqiyatli, buzuq — null va xato. Asl xatoni serverda log qiling.
8. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
Cannot query field "x" on type "Y" |
maydon sxemada yo'q yoki nomi xato | sxemani GraphiQL'da ko'ring |
must have a selection of subfields |
obyekt maydon ichki tanlovsiz so'raldi | vazifalar { matn } |
Cannot return null for non-nullable field |
resolver ! maydonga null qaytardi |
sxemani nullable qiling yoki resolverni tuzating |
| Monitoringda xato yo'q, foydalanuvchida bor | xatolar HTTP 200 bilan | errors ni ham sanash; yangi media tur |
Ma'lumot egasiId bilan keldi, egasi null |
maydon resolveri yo'q | Vazifa.egasi resolveri |
| Qiymat so'rov satriga yopishtirilgan | o'zgaruvchi ishlatilmagan | $id + variables |
Biz yuborgan to'rtinchi xato so'rovi ({ vazifalar } — ichki tanlovsiz) aynan ikkinchi qatordagi xabarni berdi: Field "vazifalar" of type "[Vazifa!]!" must have a selection of subfields. Did you mean "vazifalar { ... }"?
9. Mashqlar
Mashqlar kurs/mashqlar/29/59-graphql/ da.
1-mashq (oson): bo'shliqlarni to'ldiring
- O'qish uchun kirish tipi —
- Argument sifatida beriladigan obyekt tipi kalit so'zi —
String!dagi!ma'nosi — null- Resolver argumentlari: parent, args, , info
- Validatsiya xatosi kodi —
GRAPHQL_VALIDATION_
Yechim
- Query — o'zgartirish uchun Mutation.
- input —
input YangiVazifa { matn: String! }. - emas — non-null;
!siz maydon nullable. - context — har so'rov uchun umumiy obyekt (foydalanuvchi, loader'lar).
- FAILED — so'rov bajarilmadi,
datayo'q.
2-mashq (o'rta): qancha ortiqcha ma'lumot?
Darsdagi tahlil funksiyasini oling. ortiqcha(obyektlar, sorovMatni) yozing: u REST javobi (obyektlar — to'liq) va GraphQL javobi (faqat tanlangan maydonlar, ichma-ich tanlovsiz, birinchi daraja) baytlarini (JSON.stringify(...).length) va tejash foizini qaytarsin.
Yechim
function tahlil(matn) {
const tok = matn.match(/[{}]|\w+/g);
let i = 0;
function tanlov() {
const maydonlar = {};
i++;
while (tok[i] !== "}") {
const nom = tok[i++];
maydonlar[nom] = tok[i] === "{" ? tanlov() : true;
}
i++;
return maydonlar;
}
return tanlov();
}
function ortiqcha(obyektlar, sorovMatni) {
const maydonlar = Object.keys(tahlil(sorovMatni));
const kesilgan = obyektlar.map((o) =>
Object.fromEntries(maydonlar.map((m) => [m, o[m]])),
);
const rest = JSON.stringify(obyektlar).length;
const gql = JSON.stringify(kesilgan).length;
const foiz = Math.round((1 - gql / rest) * 100);
return { rest, gql, foiz };
}
const VAZ = Array.from({ length: 20 }, (_, i) => ({
id: i + 1,
matn: `Vazifa ${i + 1}`,
bajarildi: i % 3 === 0,
ustuvorlik: "orta",
muddat: null,
teglar: ["uy", "xarid"],
yaratilgan: "2026-10-28T09:00:00.000Z",
egasiId: 1 + (i % 5),
}));
console.log(ortiqcha(VAZ, "{ matn }"));
console.log(ortiqcha(VAZ, "{ id matn bajarildi }"));Konsolda:
{ rest: 3116, gql: 412, foiz: 87 }
{ rest: 3116, gql: 916, foiz: 71 }Ro'yxat ekrani uchun faqat matn — javob bir necha barobar kichik. Mobil tarmoqda bu sezilarli. Lekin bu hisob siqishsiz: gzip takrorlanuvchi kalitlarni yaxshi siqadi va farq kamayadi. Va REST ham ?fields=matn bilan shu natijaga erisha oladi — GraphQL'ning ustunligi buni har endpoint uchun alohida yozmaslikda.
3-mashq (qiyin): argumentli maydonlar
tahlil ni kengaytiring: maydon argument olishi mumkin — vazifa(id: 2) { matn }, vazifalar(bajarildi: false) { matn }. Argument qiymatlari: butun son, true/false, qo'shtirnoqdagi satr. Natija: { nom: { args: {...}, tanlov: {...} } }, oddiy maydon uchun true. Keyin Query resolverlari bilan bajaring.
Yechim
function tahlil(matn) {
const tok = matn.match(/"[^"]*"|[{}():,]|[\w-]+/g);
let i = 0;
function qiymat(t) {
if (t.startsWith("\"")) return t.slice(1, -1);
if (t === "true" || t === "false") return t === "true";
return Number(t);
}
function tanlov() {
const maydonlar = {};
i++;
while (tok[i] !== "}") {
const nom = tok[i++];
const args = {};
if (tok[i] === "(") {
i++;
while (tok[i] !== ")") {
const k = tok[i];
args[k] = qiymat(tok[i + 2]);
i += 3;
if (tok[i] === ",") i++;
}
i++;
}
const ichki = tok[i] === "{" ? tanlov() : null;
maydonlar[nom] = ichki ? { args, tanlov: ichki } : true;
}
i++;
return maydonlar;
}
return tanlov();
}
const VAZ = [
{ id: 1, matn: "Non olish", bajarildi: false },
{ id: 2, matn: "Hisobot", bajarildi: true },
{ id: 3, matn: "Sport", bajarildi: false },
];
const Query = {
vazifa: ({ id }) => VAZ.find((v) => v.id === id) ?? null,
vazifalar: ({ bajarildi }) =>
VAZ.filter(
(v) => bajarildi === undefined || v.bajarildi === bajarildi,
),
};
function kes(qiymat, tanlov) {
if (qiymat === null) return null;
if (Array.isArray(qiymat)) return qiymat.map((x) => kes(x, tanlov));
const juftlar = Object.keys(tanlov).map((k) => [k, qiymat[k]]);
return Object.fromEntries(juftlar);
}
function bajar(sorov) {
const data = {};
for (const [nom, m] of Object.entries(tahlil(sorov))) {
data[nom] = kes(Query[nom](m.args), m.tanlov);
}
return { data };
}
const s =
"{ vazifa(id: 2) { matn } vazifalar(bajarildi: false) { id } }";
console.log(JSON.stringify(bajar(s)));
console.log(JSON.stringify(bajar("{ vazifa(id: 9) { matn } }")));Konsolda:
{"data":{"vazifa":{"matn":"Hisobot"},"vazifalar":[{"id":1},{"id":3}]}}
{"data":{"vazifa":null}}Bitta so'rovda ikki "endpoint" — REST'da ikki HTTP so'rov bo'lardi. id: 9 uchun null — GraphQL'dagi "topilmadi". Bizning tahlilchi o'zgaruvchilar ($id), fragmentlar, izohlar va qochirilgan qo'shtirnoqlarni bilmaydi va xato so'rovda qotib qolishi mumkin. Haqiqiy loyihada so'rovni faqat graphql paketining parse va validate funksiyalari tahlil qiladi.
10. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
Bu darsda vazifalar-api ga kod qo'shilmaydi. Kanon GraphQL Yoga'ni tajribada sinadi: sxema, resolver'lar, noma'lum maydon xatosi va NOT_FOUND mutation xatosi (HTTP 200), 20 vazifali so'rov mediana 2.26 ms (lokal, bitta o'lchov). vazifalar-api REST bo'lib qoladi: bitta frontend, oddiy CRUD — GraphQL'ning kuchli tomonlari bu yerda kerak emas. NestJS'da GraphQL moduli bor (30-qism) — u yerda code-first sxema bilan qayta uchrashamiz.
O'zingiz sinab ko'ring: alohida papkada npm i graphql-yoga@5.24.6 graphql@17.0.2, darsdagi sxema va resolver'lar bilan server ko'taring. graphiql: true qilib, brauzerda http://127.0.0.1:4000/graphql ni oching — avtoto'ldirish va hujjat panelida sxemani ko'ring.
11. Real ishda
- GitHub API v4, Shopify, Facebook — ommaviy GraphQL API'lar; GitHub'da REST va GraphQL yonma-yon.
- Headless CMS (Strapi, Contentful, Hygraph) — kontentni GraphQL orqali beradi (Tayyor backend'lar: BaaS va headless CMS).
- Mobil ilovalar — bitta ekran uchun bitta so'rov; Apollo Client, urql, Relay klientlari.
- Federation — bir nechta servis sxemalari bitta "supergraph" ga birlashadi (Apollo Federation, GraphQL Mesh).
- Intervyuda: "GraphQL REST'dan nimasi bilan farq qiladi?", "Resolver nima?", "Nega GraphQL xatolari 200 bilan keladi?", "Over-fetching va under-fetching?"
Xulosa
- GraphQL — bitta endpoint, tiplangan sxema (SDL), mijoz maydonlarni o'zi tanlaydi; javob so'rov shaklida.
Query— o'qish,Mutation— o'zgartirish,input— kirish obyekti;!— non-null, sukut nullable.- Resolver
(parent, args, context, info); yozilmagan maydon uchun sukut resolverparent[nom]. - Qiymatlar —
variablesorqali, so'rov satriga emas. - Xatolar
errorsda; validatsiya xatosidadatayo'q;application/jsonda 200,application/graphql-response+jsonda 400. - Ruxsat har maydon darajasida; introspection va og'ir so'rovlar — xavf.
Keyingi dars: GraphQL chuqur — N+1 va DataLoader, chuqurlik limiti, cursor sahifalash, subscriptions va persisted queries.
Manbalar
- GraphQL spetsifikatsiyasi: https://spec.graphql.org/
- GraphQL over HTTP (qoralama): https://graphql.github.io/graphql-over-http/draft/
- GraphQL Yoga hujjati: https://the-guild.dev/graphql/yoga-server/docs
- Apollo Server hujjati: https://www.apollographql.com/docs/apollo-server
- graphql-js: https://graphql-js.org/
- Kanon tajribasi (IlmHamroh, 2026-10-27): GraphQL Yoga 5.24.6, graphql 17.0.2, Node 24.21.0. Darsdagi server chiqishlari,
Accepttajribasi, JS bloklari va mashq yechimlari — biz ishga tushirdik (2026-10-28, o'sha versiyalar). Apollo Server — sinalmagan.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!