Mundarija (30)
- Bu darsda
- 1. Nega bu kerak?
- 2. O'rnatish va ikki tuzoq
- 2.1 Paketlar
- 2.2 Birinchi tuzoq: peer ziddiyati
- 2.3 Ikkinchi tuzoq: jim yiqilish
- 3. Modulni ulash
- 4. Turlar: klassdan SDL'gacha
- 4.1 @ObjectType va @Field
- 4.2 Enum va custom scalar
- 4.3 @InputType va @ArgsType
- 5. Resolver'lar
- 5.1 @Resolver, @Query, @Mutation
- 5.2 So'rovlar
- 6. Xatolar
- 6.1 Validatsiya — so'rov bajarilmaydi
- 6.2 Resolver ichidagi xato
- 6.3 Hujumchi nigohi: stacktrace va introspection
- 7. JS modelida code-first
- 8. Kanon tajribasi: nega loyihaga kirmaydi
- 9. Express'da qanday edi → Nest'da qanday
- 10. Ko'p uchraydigan xatolar
- 11. Mashqlar
- 1-mashq (oson): bo'shliqlarni to'ldiring
- 2-mashq (o'rta): enum generatori
- 3-mashq (qiyin): argumentli query
- 12. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
- 13. Real ishda
- Xulosa
- Manbalar
GraphQL code-first NestJS'da: @ObjectType, @Resolver, @Query va @Mutation
Qisqacha: Code-first — GraphQL sxemasini qo'lda yozmasdan, TypeScript klasslaridan yasash. Klassga
@ObjectType(), maydonlariga@Field()qo'yasiz, resolver metodlariga@Query()va@Mutation()— Nest ishga tushgandaschema.gqlfaylini o'zi yozadi. Ichida Apollo Server 5 (yoki Fastify uchun Mercurius) ishlaydi. Shu darsda o'rnatishdagi ikki haqiqiy tuzoqni ham ko'ramiz: npm peer ziddiyati va Express 5 uchun alohida integratsiya paketi — u yo'q bo'lsa, ilova hech qanday xabarsiz yiqiladi.
Bu darsda
@nestjs/graphqlva Apollo drayverini o'rnatib, code-first sxema yasaysiz.@ObjectType,@Field,@InputType,@ArgsType, enum va custom scalar yozasiz.- Nest yaratgan
schema.gqlni o'qib, TypeScript va GraphQL turlarini moslaysiz. - GraphQL xatolarining uch turini (validatsiya, resolver, HTTP) va stacktrace sizishini bilasiz.
- Nega GraphQL
vazifalar-apikanoniga kirmaganini o'lchov bilan tushuntirasiz.
Oldin bilishingiz kerak: GraphQL asoslari, GraphQL chuqur, Mapped types va nested validatsiya, Server-Sent Events NestJS'da.
Oldingi darsda loyiha bosqichini commit qildik. Bu darsdan boshlab besh dars Nest ekotizimining katta mavzulariga bag'ishlangan: GraphQL, mikroservislar, CQRS. Ular vazifalar-api ga kirmaydi — har birini alohida tajribada sinab, nega kanonda yo'qligini o'lchov bilan aytamiz. Lekin Nest ishida ularni albatta uchratasiz.
1. Nega bu kerak?
29-qismda GraphQL'ni GraphQL Yoga bilan yozgandik (GraphQL asoslari). Ish ikki joyda bo'lardi. Birinchisi — SDL matni:
type Vazifa {
id: Int!
matn: String!
bajarildi: Boolean!
}Ikkinchisi — TypeScript turi va resolver'lar. Ikkalasi ham "vazifa nima" degan savolga javob beradi. Biriga maydon qo'shib, ikkinchisini unutsangiz — tur va sxema ajralib ketadi. TypeScript buni sezmaydi: SDL — oddiy satr.
Bu yondashuvning nomi — schema-first (avval sxema): SDL — asosiy manba, kod unga moslanadi. Nest buni ham qo'llaydi (typePaths va SDL'dan TS turlarini yasash).
Teskari yondashuv — code-first (avval kod): asosiy manba — TypeScript klassi. SDL undan avtomatik yasaladi. Tur bitta joyda yoziladi, sxema doim kod bilan mos. Nest jamoalari ko'pincha shuni tanlaydi, chunki dekoratorlar Nest'ning tabiiy tili.
O'xshatish: chizma va bino. Schema-first — avval chizma, keyin quruvchilar unga qarab quradi; chizma yangilansa-yu, bino yangilanmasa — farq. Code-first — bino qurilgach, uni lazer bilan o'lchab chizma o'zi chiqariladi. Chizma doim haqiqiy binoga mos.
2. O'rnatish va ikki tuzoq
2.1 Paketlar
Kanon tajribasida ishlatilgan versiyalar:
npm i --save-exact @nestjs/graphql@14.0.3 @nestjs/apollo@14.0.3 \
@apollo/server@5.5.1 graphql@17.0.2 \
@as-integrations/express5@1.1.2Har paket nima uchun:
| Paket | Vazifasi |
|---|---|
@nestjs/graphql |
dekoratorlar, sxema generatori |
@nestjs/apollo |
Apollo drayveri |
@apollo/server |
GraphQL HTTP server |
graphql |
GraphQL'ning JS yadrosi |
@as-integrations/express5 |
Apollo'ni Express 5 ga ulash |
2.2 Birinchi tuzoq: peer ziddiyati
Biz shu versiyalarni demo loyihaga o'rnatganimizda npm to'xtadi (biz ishga tushirdik, npm 11):
npm error Could not resolve dependency:
npm error peer graphql@"^16.11.0" from @apollo/server@5.5.1
npm error node_modules/@apollo/server
npm error @apollo/server@"5.5.1" from the root project
npm error peer @apollo/server@"^5.0.0" from @nestjs/apollo@14.0.3Tarjimasi: "bog'liqlikni hal qilib bo'lmadi: @apollo/server@5.5.1 graphql@^16.11.0 ni talab qiladi". Peer bog'liqlik (peer dependency) — paket o'zi o'rnatmaydigan, lekin sizning loyihangizda bo'lishini kutadigan paket (versiya oralig'i semver qoidalari bilan yoziladi). @nestjs/graphql 14.0.3 graphql 16 ni ham, 17 ni ham qabul qiladi. Apollo Server 5.5.1 esa faqat 16 ni. Biz graphql@17.0.2 so'radik — ziddiyat.
Ikki yo'l bor:
graphqlning 16.x versiyasini olish — hamma peer talablari bajariladi. Bu xavfsiz yo'l.--legacy-peer-depsbilan o'rnatish — npm peer tekshiruvini o'tkazib yuboradi. Kanon tajribasi 17.0.2 bilan ishlagan, bizda ham shu bayroq bilan hammasi ishladi. Lekin bu "Apollo 17 bilan sinalmagan" degan ogohlantirishni o'chirish degani.
Productionda birinchisini tanlang. Darsdagi chiqishlar ikkinchi yo'l bilan olingan — kanon versiyalariga mos bo'lsin deb.
2.3 Ikkinchi tuzoq: jim yiqilish
Nest 12 Express 5 ustida ishlaydi. Apollo Server 5 esa Express 5 ni o'zi bilmaydi — unga @as-integrations/express5 paketi kerak. Kanon tajribasida shu paket yo'q edi va natija dahshatli bo'ldi: jarayon hech qanday xabarsiz exit 1 bilan to'xtadi.
Biz buni qayta tekshirdik: paketni vaqtincha olib tashlab, demoni ikki marta ishga tushirdik. Birinchisida logger: false (kanon tajribasidagidek):
exit=1Bo'sh ekran, faqat chiqish kodi. Ikkinchisida logger yoqilgan:
ERROR [PackageLoader] The "@as-integrations/express5" package is missing. Please, make sure to install it to use GraphQLModule.
exit=1Tarjimasi: "@as-integrations/express5 paketi yo'q. GraphQLModule'dan foydalanish uchun uni o'rnating". Nest xatoni logga yozib, jarayonni o'zi to'xtatadi — logger o'chiq bo'lsa, xabar yo'qoladi. Saboq ikki qavatli: paketni o'rnating va testlarda ham Nest logger'ini butunlay o'chirmang — kamida ["error"] qoldiring.
3. Modulni ulash
import { ApolloDriver } from "@nestjs/apollo";
import type { ApolloDriverConfig } from "@nestjs/apollo";
import { GraphQLModule } from "@nestjs/graphql";
@Module({
imports: [
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: join(process.cwd(), "dist", "schema.gql"),
sortSchema: true,
}),
],
providers: [VazifaResolver, SanaScalar],
})
class Ilova {}Uch sozlama:
driver— qaysi GraphQL server.ApolloDriver— Express (va Fastify) uchun;MercuriusDriver(@nestjs/mercurius) — faqat Fastify, tezroq deb aytiladi. Mercurius'ni sinamadik.autoSchemaFile— code-first'ni yoqadi. Yo'l bersangiz, SDL shu faylga yoziladi;truebersangiz — faqat xotirada.sortSchema— turlar va maydonlar alifbo tartibida. Usiz tartib — klasslar ro'yxatga olingan tartib, git diff'lar shovqinli bo'ladi.
GraphQL endpoint sukut bo'yicha /graphql — bitta yo'l, faqat POST (va GET). REST'dagi o'nlab marshrutlar o'rnida.
4. Turlar: klassdan SDL'gacha
4.1 @ObjectType va @Field
Demo loyihadagi asosiy tur:
@ObjectType({ description: "Bitta vazifa" })
class Vazifa {
@Field(() => ID) id!: number;
@Field() matn!: string;
@Field() bajarildi!: boolean;
@Field(() => [String]) teglar!: string[];
@Field(() => Muhimlik) muhimlik!: Muhimlik;
@Field({ nullable: true, description: "Muddat (bo'lmasa — null)" })
muddat?: Date;
yaratildi!: Date; // @Field yo'q — sxemaga kirmaydi
}Nest yaratgan schema.gql dan shu turning qismi (biz ishga tushirdik):
"""Bitta vazifa"""
type Vazifa {
bajarildi: Boolean!
id: ID!
matn: String!
"""Muddat (bo'lmasa — null)"""
muddat: Sana
muhimlik: Muhimlik!
teglar: [String!]!
}Klass va SDL'ni qatorma-qator solishtiring:
@Field() matn!: string→matn: String!. Turni Nest TypeScript metama'lumotidan o'zi oldi (design:type— legacy dekoratorlar).!— sukut bo'yicha maydon majburiy (non-null).@Field(() => ID)— turni qo'lda berish. Metama'lumotnumberdeydi, lekin GraphQL'da uIDbo'lsin. Massiv uchun ham qo'lda kerak:() => [String]. Sabab —design:typemassivni faqatArraydeb yozadi, ichidagi tur yo'qoladi. Biz sinab ko'rdik:teglardagi() => [String]ni olib tashlasak, ilova ishga tushmaydi —UndefinedTypeError: Undefined type error. Make sure you are providing an explicit type for the "teglar" of the "Vazifa" class.("Vazifaklassidagiteglaruchun turni aniq bering").nullable: true→!sizmuddat: Sana.description— SDL'dagi"""…"""izohi; GraphiQL kabi vositalar uni ko'rsatadi.yaratildi—@Fieldyo'q, SDL'da yo'q. Klassda bor, mijozga ko'rinmaydi. Ichki maydonlarni (parol hash'i,egasi_id) shu yo'l bilan yashirasiz.
4.2 Enum va custom scalar
enum Muhimlik {
PAST = "past",
ODDIY = "oddiy",
YUQORI = "yuqori",
}
registerEnumType(Muhimlik, {
name: "Muhimlik", description: "Vazifa muhimligi",
});TypeScript enum ni GraphQL ko'rmaydi — registerEnumType uni ro'yxatga oladi. SDL'da kalitlar chiqadi (ODDIY, PAST, YUQORI), qiymatlar ("oddiy") esa server ichida qoladi. Javobda ham kalit keladi: "muhimlik":"ODDIY".
Custom scalar — o'z oddiy turingiz. GraphQL'da sana turi yo'q; Sana ni o'zimiz yasadik:
@Scalar("Sana", () => Date)
class SanaScalar implements CustomScalar<string, Date> {
description = "ISO 8601 sana (UTC)";
serialize(v: unknown): string {
return (v as Date).toISOString(); // server → mijoz
}
parseValue(v: unknown): Date {
return new Date(String(v)); // variables'dan kelgan qiymat
}
parseLiteral(ast: ValueNode): Date {
// so'rov matnining ichida yozilgan qiymat
if (ast.kind !== Kind.STRING) {
throw new TypeError("Sana satr bo'lishi kerak");
}
return new Date(ast.value);
}
}Uch metod — uch yo'nalish. serialize — javobga chiqishda (Date → satr). parseValue — so'rov o'zgaruvchilaridan (variables) kirishda. parseLiteral — so'rov matniga to'g'ridan yozilgan qiymatda. () => Date — "TypeScript'da Date turli maydon shu scalar bo'lsin" degan bog'lanish. Shuning uchun muddat?: Date SDL'da Sana bo'ldi. Scalar providers ga qo'shiladi.
4.3 @InputType va @ArgsType
Kiruvchi ma'lumot uchun alohida tur kerak: GraphQL'da chiqish turi (type) va kirish turi (input) har xil.
@InputType()
class YangiVazifa {
@Field() matn!: string;
@Field(() => Muhimlik, { defaultValue: Muhimlik.ODDIY })
muhimlik!: Muhimlik;
@Field({ nullable: true }) muddat?: Date;
}
@ArgsType()
class RoyxatArgs {
@Field({ nullable: true }) bajarildi?: boolean;
@Field(() => Int, { defaultValue: 10 }) limit!: number;
}input YangiVazifa {
matn: String!
muddat: Sana
muhimlik: Muhimlik! = ODDIY
}@InputType — bitta argument sifatida keladigan obyekt (yarat(kirish: YangiVazifa!)). @ArgsType — argumentlarni yoyib beradi: vazifalar(bajarildi: Boolean, limit: Int! = 10). Int ni qo'lda berdik: TypeScript'da number, GraphQL'da esa Int va Float alohida. Sukut — Float.
Tekshirib ko'ring:
@Field() narx!: numbermaydoni SDL'da qanday tur bo'ladi va bu so'm uchun to'g'rimi?
Javob
narx: Float!. Nest number ni sukut bo'yicha Float qiladi. Pul uchun bu xavfli: kasr sonlar aniq emas (Pul bilan ishlash JS'da). Pulni eng kichik birlikda butun son qilib saqlang va @Field(() => Int) deb yozing — tiyinda yoki so'mda.
5. Resolver'lar
5.1 @Resolver, @Query, @Mutation
@Resolver(() => Vazifa)
class VazifaResolver {
@Query(() => [Vazifa], { description: "Vazifalar ro'yxati" })
vazifalar(@Args() { bajarildi, limit }: RoyxatArgs): Vazifa[] {
return baza
.filter((v) => bajarildi === undefined
|| v.bajarildi === bajarildi)
.slice(0, limit);
}
@Query(() => Vazifa, { nullable: true })
vazifa(@Args("id", { type: () => ID }) id: string) {
return baza.find((v) => v.id === Number(id));
}
@Mutation(() => Vazifa)
yarat(@Args("kirish") k: YangiVazifa): Vazifa {
const v: Vazifa = {
id: baza.length + 1, matn: k.matn, bajarildi: false,
teglar: [], muhimlik: k.muhimlik, muddat: k.muddat,
yaratildi: new Date(),
};
baza.push(v);
return v;
}
}Resolver — Nest'da controller'ning GraphQL dagi o'rnidosh. @Resolver(() => Vazifa) — "bu klass Vazifa turi bilan ishlaydi". @Query — o'qish, @Mutation — o'zgartirish (GraphQL asoslari). @Args() — argumentlar; @Args("id", { type: () => ID }) — bitta nomli argument. Resolver ham provider: u providers ga qo'shiladi va xizmatlarni constructor orqali oladi — DI REST'dagidek.
5.2 So'rovlar
Uch so'rov yubordik (biz ishga tushirdik, NODE_ENV=production):
--- 1) query
→ 200 {"data":{"vazifalar":[{"id":"1","matn":"Non olish","muhimlik":"ODDIY","muddat":null}]}}
--- 2) mutation + variables
→ 200 {"data":{"yarat":{"id":"3","matn":"Sut olish","muhimlik":"ODDIY","muddat":"2026-11-04T04:00:00.000Z"}}}
--- 3) nullable: yo'q id
→ 200 {"data":{"vazifa":null}}Birinchi so'rov: { vazifalar(bajarildi: false) { id matn muhimlik muddat } }. Mijoz to'rt maydon so'radi — to'rttasi keldi. Ikki narsaga qarang: "id":"1" — satr. Biz number qaytardik, lekin GraphQL ID turini doim satr qilib yuboradi (kanon tajribasida ham shunday). Frontendda v.id === 1 ishlamaydi. "muddat":null — nullable maydon, qiymat yo'q.
Ikkinchi so'rov o'zgaruvchilar bilan: mutation ($k: YangiVazifa!) { yarat(kirish: $k) { … } }, variables: { k: { matn: "Sut olish", muddat: "2026-11-04T04:00:00.000Z" } }. muhimlik berilmadi — sukut ODDIY ishladi. muddat satr bo'lib keldi, parseValue uni Date ga aylantirdi, javobda serialize qayta satr qildi.
Uchinchisi — nullable: true li vazifa(id: "99"): topilmadi, null. Xato emas.
6. Xatolar
6.1 Validatsiya — so'rov bajarilmaydi
--- 5) noma'lum maydon
→ 400 {"errors":[{"message":"Cannot query field \"parol\" on type \"Vazifa\".","locations":[{"line":1,"column":15}],"extensions":{"code":"GRAPHQL_VALIDATION_FAILED"}}]}
--- 6) @Field yo'q maydon
→ 400 {"errors":[{"message":"Cannot query field \"yaratildi\" on type \"Vazifa\". Did you mean \"bajarildi\"?",…}]}
--- 7) noto'g'ri tur
→ 400 {"errors":[{"message":"Int cannot represent non-integer value: \"ko'p\"",…}]}(Oxirgi ikkitasida locations va extensions ni qisqartirdik — birinchisidagidek.) Uchala holatda resolver chaqirilmadi: GraphQL so'rovni sxemaga solishtirib, bajarishdan oldin rad etdi. Status — 400. "Did you mean" — GraphQL yaqin nomni taklif qiladi. 6-holat muhim: klassda yaratildi bor, lekin @Field yo'q — mijoz uni so'ray olmaydi.
6.2 Resolver ichidagi xato
@Query(() => Vazifa)
vazifaMajburiy(@Args("id", { type: () => ID }) id: string) {
const v = baza.find((x) => x.id === Number(id));
if (!v) throw new NotFoundException(`Vazifa ${id} topilmadi`);
return v;
}--- 4) NotFoundException resolverda
→ 200 {"errors":[{"message":"Vazifa 99 topilmadi","locations":[{"line":1,"column":3}],"path":["vazifaMajburiy"],"extensions":{"code":"INTERNAL_SERVER_ERROR","status":404,"originalError":{"message":"Vazifa 99 topilmadi","error":"Not Found","statusCode":404}}}],"data":null}Uch g'alati narsa. HTTP status — 200: GraphQL'da so'rov bajarildi, xato javob ichida (29-qismdagi "200 yoki 400?" mavzusi). code — INTERNAL_SERVER_ERROR, garchi bu 404 bo'lsa ham: Apollo Nest'ning NotFoundException ini tanimaydi. Nest esa status: 404 va originalError ni qo'shib qo'ygan. Mijoz uchun eng ishonchli joy — extensions.status. Toza yechim — GraphQLError ni o'z code ingiz bilan tashlash (extensions: { code: "NOT_FOUND" }) yoki formatError sozlamasida Nest xatolarini xaritalash.
6.3 Hujumchi nigohi: stacktrace va introspection
Xuddi shu 4-so'rovni NODE_ENV siz (development rejimida) yuborganimizda, javobda yana bir maydon bor edi (qisqartirdik):
"extensions":{"code":"INTERNAL_SERVER_ERROR","stacktrace":["NotFoundException: Vazifa 99 topilmadi"," at VazifaResolver.vazifaMajburiy (file:///…/demo/dist/t-gql.js:150:19)"," at file:///…/node_modules/@nestjs/core/helpers/external-context-creator.js:80:33",…]Bu — serverning xato izi: fayl yo'llari, kutubxona nomlari va versiyalari. Hujumchi uchun xazina: qaysi kutubxona, qaysi papka, qaysi foydalanuvchi nomi. Apollo NODE_ENV=production bo'lmasa, izni sukut bo'yicha qo'shadi. Production'da NODE_ENV=production ni unutmang yoki includeStacktraceInErrorResponses: false ni aniq yozing.
Ikkinchi narsa — introspection: sxemaning o'zini so'rash ({ __schema { … } }). Biz ikki rejimda sinadik:
NODE_ENV=development
introspection: 200 {"data":{"__schema":{"queryType":{"name":"Query"}}}}
NODE_ENV=production
introspection: 400 {"errors":[{"message":"GraphQL introspection is not allowed by Apollo Server, but the query contained __schema or __type. To enable introspection, pass introspection: true to ApolloServer in production",…}]}Tarjimasi: "Apollo Server GraphQL introspection'ga ruxsat bermaydi, lekin so'rovda __schema yoki __type bor. Yoqish uchun production'da ApolloServer'ga introspection: true bering". Ochiq API (masalan GitHub) introspection'ni ataylab yoqadi. Ichki API'da esa u butun "xaritani" beradi — o'chiq qolsin.
Uchinchisi — CSRF himoyasi. Brauzer formasi bilan yuboriladigan oddiy GET so'rovni Apollo to'sdi:
GET (sarlavhasiz): 400 {"errors":[{"message":"This operation has been blocked as a potential Cross-Site Request Forgery (CSRF). Please either specify a 'content-type' header (with a type that is not one of application/x-www-form-urlencoded, multipart/form-data, text/plain) or provide a non-empty value for one of the following headers: x-apollo-operation-name, apollo-require-preflight\n","extensions":{"code":"BAD_REQUEST"}}]}Tarjimasi: "Bu amal ehtimoliy CSRF sifatida bloklandi. content-type sarlavhasini bering yoki x-apollo-operation-name / apollo-require-preflight sarlavhalaridan birini". Bu Apollo'ning sukut himoyasi (Tokenni qayerda saqlash va CSRF darsidagi "oddiy so'rov" g'oyasi): brauzer boshqa saytdan sarlavhasiz so'rovni yubora oladi, content-type: application/json li so'rovni esa preflight'siz yubora olmaydi.
7. JS modelida code-first
Code-first'ning yuragi oddiy: maydonlar ro'yxatidan SDL matnini yig'ish. Nest buni dekoratorlar yozgan metama'lumotdan qiladi, biz esa ro'yxatni qo'lda beramiz. Blok brauzerda ishlaydi:
// Code-first'ning yuragi: klass ma'lumotidan SDL matni
const turlar = new Map(); // tur nomi → maydonlar
function objectType(nom, maydonlar) {
turlar.set(nom, maydonlar);
}
function turYoz({ tur, nullable = false }) {
const asos = Array.isArray(tur) ? `[${tur[0]}!]` : tur;
return nullable ? asos : `${asos}!`;
}
function sdl() {
return [...turlar].map(([nom, maydonlar]) => {
const qatorlar = maydonlar
.toSorted((a, b) => a.nom.localeCompare(b.nom)) // sortSchema
.map((m) => ` ${m.nom}: ${turYoz(m)}`);
return `type ${nom} {\n${qatorlar.join("\n")}\n}`;
}).join("\n\n");
}
objectType("Vazifa", [
{ nom: "id", tur: "ID" },
{ nom: "matn", tur: "String" },
{ nom: "bajarildi", tur: "Boolean" },
{ nom: "teglar", tur: ["String"] },
{ nom: "muddat", tur: "Sana", nullable: true },
]);
console.log(sdl());Konsolda:
type Vazifa {
bajarildi: Boolean!
id: ID!
matn: String!
muddat: Sana
teglar: [String!]!
}Nest yaratgan schema.gql bilan solishtiring — izoh va muhimlik dan tashqari bir xil. Modeldagi uch qoida Nest'niki: sukut — non-null (!), massiv elementi ham non-null ([String!]!), sortSchema — alifbo tartibi. Soddalashtirilgan joyi: Nest turni design:type dan o'zi oladi, biz esa tur ni yozdik; description, input va argumentlar yo'q (3-mashqda argumentlarni qo'shamiz).
flowchart LR
K["TS klass + @Field"] --> M["metama'lumot"]
M --> G["Nest sxema generatori"]
G --> S["schema.gql (SDL)"]
G --> A["Apollo Server"]
A --> R["Resolver metodlari"]Diagrammada chap tomon — siz yozadigan yagona joy. Qolgani ishga tushishda avtomatik.
8. Kanon tajribasi: nega loyihaga kirmaydi
Kanon vazifalar-api ga GraphQL qo'shmadi. Alohida kichik tajriba qildi (@nestjs/graphql va @nestjs/apollo 14.0.3, Apollo Server 5.5.1, graphql 17.0.2):
1) GraphQL (Apollo 5, code-first): ishga tushish 2190 ms
query → 200 {"data":{"vazifalar":[{"id":"1","matn":"Non olish","izohSoni":2,"izohlar":[{"matn":"Tez"},{"matn":"Bugun"}]},{"id":"2","matn":"Hisobot","izohSoni":0,"izohlar":[]}]}}
; izohlar resolver chaqiruvi: 2 (N+1)
mutation → 200 {"data":{"yarat":{"id":"3","matn":"Sut","bajarildi":false}}}Ishga tushish 2 190 ms — kanon mashinasi boshqa ishlar bilan band edi. Biz bo'sh mashinada demoni ikki marta ishga tushirdik: birinchisi 10 930 ms (disk keshi sovuq), keyingilari ~330 ms. Ya'ni raqam juda o'zgaruvchan; muhimi — GraphQL modulining o'zi sxemani har startda yasaydi va bu vaqt oladi. izohlar resolver chaqiruvi: 2 (N+1) — keyingi darsning mavzusi.
Nega kanonga kirmadi:
- Ikkinchi API yuzasi. REST API to'liq, testlangan, frontend REST bilan ishlaydi. GraphQL qo'shish — o'sha ma'lumotga ikkinchi eshik: auth, rate limit, keshlash, log — hammasini ikki marta.
- Foyda kichik. GraphQL'ning kuchi — ko'p xil mijozlar har xil maydon so'raganda. Vazifalar ilovasida bitta frontend va oddiy ro'yxat.
- Narx. 5 ta paket, sxema generatsiyasi startda, peer ziddiyati va Express 5 integratsiyasi kabi tuzoqlar.
Bu "GraphQL yomon" degani emas. Mobil ilova, veb va hamkorlar bitta API'dan har xil ma'lumot olsa — GraphQL o'zini oqlaydi.
9. Express'da qanday edi → Nest'da qanday
| 29-qism (Yoga, schema-first) | Nest code-first | |
|---|---|---|
| Sxema manbasi | SDL satri | TS klass + dekorator |
| Tur va sxema mosligi | qo'lda | avtomatik |
| Resolver | oddiy obyekt | @Resolver klass, DI bilan |
| Validatsiya xatolari | GraphQL o'zi | GraphQL o'zi (bir xil) |
10. Ko'p uchraydigan xatolar
| Xato | Nima bo'ladi | Tuzatish |
|---|---|---|
@as-integrations/express5 yo'q |
exit 1 (logger o'chiq bo'lsa — jim) |
paketni o'rnating |
@Field() massivda |
UndefinedTypeError — ilova ishga tushmaydi |
@Field(() => [String]) |
number → Float |
butun son kutilgan joyda kasr | @Field(() => Int) |
ID ni son deb solishtirish |
"1" === 1 → false |
Number(id) |
NODE_ENV production emas |
javobda stacktrace | NODE_ENV=production |
Birorta resolver providers da yo'q |
Query root type must be provided. |
modulga qo'shing |
11. Mashqlar
Mashqlar kurs/mashqlar/30/43-graphql/ da.
1-mashq (oson): bo'shliqlarni to'ldiring
- Klassni GraphQL chiqish turiga aylantiradigan dekorator —
- Kirish obyekti uchun dekorator —
IDturidagi maydon javobda qanday JS turida keladi? —- Express 5 bilan Apollo uchun kerak integratsiya paketi —
@as-integrations/ - Production'da Apollo sukut bo'yicha o'chiradigan imkoniyat (sxemani so'rash) —
Yechim
- @ObjectType —
@ObjectType() class Vazifa { @Field() matn!: string }. - @InputType —
yarat(kirish: YangiVazifa!); argumentlarni yoyish uchun@ArgsType. - string —
"id":"1". - express5 — usiz jarayon
exit 1. - introspection —
NODE_ENV=productionda 400INTROSPECTION_DISABLED.
2-mashq (o'rta): enum generatori
Modelga registerEnumType(obyekt, { name }) qo'shing va sdl() enum'larni chiqarsin: kalitlar alifbo tartibida, har biri alohida qatorda. Yana bitta yordamchi yozing: ichki qiymatdan ("yuqori") GraphQL nomini (YUQORI) topsin. Ishora: Object.keys (Object metodlari) va find; toSorted() argumentsiz satrlarni tartiblaydi (toSorted).
Yechim
const enumlar = new Map(); // nom → qiymatlar obyekti
function registerEnumType(obyekt, { name }) {
enumlar.set(name, obyekt);
}
function sdl() {
return [...enumlar].map(([nom, obyekt]) => {
// GraphQL'ga KALITLAR chiqadi, qiymatlar emas
const qatorlar = Object.keys(obyekt).toSorted()
.map((k) => ` ${k}`);
return `enum ${nom} {\n${qatorlar.join("\n")}\n}`;
}).join("\n\n");
}
const Muhimlik = { PAST: "past", ODDIY: "oddiy", YUQORI: "yuqori" };
registerEnumType(Muhimlik, { name: "Muhimlik" });
console.log(sdl());
// javobda: ichki qiymat ("yuqori") → GraphQL nomi (YUQORI)
const nomi = (q) =>
Object.keys(Muhimlik).find((k) => Muhimlik[k] === q);
console.log(nomi("yuqori"));Konsolda:
enum Muhimlik {
ODDIY
PAST
YUQORI
}
YUQORINatija Nest yaratgan enum bilan bir xil. Ikkinchi qism — Nest javob yuborishda qiladigan ish: server ichida "yuqori", mijozga YUQORI. Kirishda esa teskarisi. Shu sababli bazadagi qiymat ("yuqori") va API'dagi nom (YUQORI) mustaqil o'zgarishi mumkin.
3-mashq (qiyin): argumentli query
type Query blokini yasaydigan funksiya yozing. Har query: nom, tur, nullable, args (har biri nom, tur, nullable, defaultValue). Natija Nest chiqishidagidek bo'lsin: vazifalar(bajarildi: Boolean, limit: Int! = 10): [Vazifa!]! va vazifa(id: ID!): Vazifa. Argument bo'lmasa — qavs ham yo'q. Ishora: darsdagi turYoz ni qayta ishlating; sukut qiymat uchun JSON.stringify(10) → 10.
Yechim
function turYoz({ tur, nullable = false }) {
const asos = Array.isArray(tur) ? `[${tur[0]}!]` : tur;
return nullable ? asos : `${asos}!`;
}
function argYoz(a) {
const sukut = a.defaultValue === undefined
? "" : ` = ${JSON.stringify(a.defaultValue)}`;
return `${a.nom}: ${turYoz(a)}${sukut}`;
}
function queryYoz(q) {
const args = q.args.length > 0
? `(${q.args.map(argYoz).join(", ")})` : "";
return ` ${q.nom}${args}: ${turYoz(q)}`;
}
const queries = [
{
nom: "vazifalar", tur: ["Vazifa"],
args: [
{ nom: "bajarildi", tur: "Boolean", nullable: true },
{ nom: "limit", tur: "Int", defaultValue: 10 },
],
},
{ nom: "vazifa", tur: "Vazifa", nullable: true,
args: [{ nom: "id", tur: "ID" }] },
];
console.log(`type Query {\n${queries.map(queryYoz).join("\n")}\n}`);Konsolda:
type Query {
vazifalar(bajarildi: Boolean, limit: Int! = 10): [Vazifa!]!
vazifa(id: ID!): Vazifa
}Nest chiqishi bilan solishtiring: vazifalar(bajarildi: Boolean, limit: Int! = 10): [Vazifa!]! — aynan shu. limit sukut qiymatli bo'lsa ham Int! — mijoz uni bermasa, sukut ishlaydi, lekin null yubora olmaydi. Enum sukutida (= ODDIY) JSON.stringify qo'shtirnoq qo'shib yuborardi — haqiqiy generator enum qiymatini alohida yozadi.
12. Loyiha qadami: kanon tajribasi, loyihaga kirmaydi
Bu darsda vazifalar-api o'zgarmaydi. Xohlasangiz, alohida papkada demo yarating (npx @nestjs/cli@12.0.9 new gql-demo), darsdagi paketlarni o'rnating va Vazifa turini VazifalarService ga ulang: resolver xizmatni constructor orqali oladi — REST controller'idagidek. Shunda bitta xizmat ikki API'ga xizmat qilishini ko'rasiz. Bu demo keyingi darsda ham kerak bo'ladi.
13. Real ishda
- Qayerda uchraydi: ko'p mijozli mahsulotlar (veb, iOS, Android, hamkor API), BFF (backend for frontend) qatlamlari, CMS'lar. Nest + GraphQL — e-commerce va fintech jamoalarida keng tarqalgan juftlik.
- Code-first yoki schema-first: jamoa sxemani dizaynerlar va frontendchilar bilan oldindan kelishsa — schema-first; backend boshqarsa — code-first.
- Intervyu savollari: "Code-first va schema-first farqi?", "
IDturi nega satr?", "GraphQL'da xatolar qanday qaytadi?", "Production'da nimani o'chirasiz?" (introspection, stacktrace).
Xulosa
- Code-first: TS klass +
@ObjectType/@Field→ Nestschema.gqlni o'zi yasaydi; tur bitta joyda. @Field(() => Tur)— massiv,ID,Intva enum uchun turni qo'lda bering; sukut — non-null,number—Float.@InputType— kirish obyekti,@ArgsType— yoyilgan argumentlar; custom scalar —@Scalarva uch metod.- Validatsiya xatosi — 400, resolver xatosi — 200 +
errors;extensions.statusNest'dan. - Production:
NODE_ENV=production— stacktrace va introspection o'chadi; Apollo CSRF himoyasi sukut bo'yicha yoqiq. - O'rnatish tuzoqlari:
graphqlpeer ziddiyati va@as-integrations/express5(usiz — jimexit 1).
Keyingi dars: GraphQL chuqur NestJS'da — @ResolveField va N+1, so'rov doirasidagi DataLoader, guard'lar, murakkablik chegarasi va subscriptions.
Manbalar
- NestJS hujjati — GraphQL Quick start, Resolvers, Scalars, Enums: https://docs.nestjs.com/graphql/quick-start
- Apollo Server 5 — Error handling, introspection, CSRF prevention: https://www.apollographql.com/docs/apollo-server/
@as-integrations/express5: https://github.com/apollo-server-integrations/apollo-server-integration-express5- GraphQL spetsifikatsiyasi — ID, non-null, input types: https://spec.graphql.org/
- Kanon (IlmHamroh, 2026-10-28): GraphQL tajribasi — ishga tushish 2 190 ms, N+1 (2 chaqiruv), validatsiya 400,
@as-integrations/express5topilmasi. Demo (sxema, enum, scalar, so'rovlar, xatolar, stacktrace, introspection, CSRF, peer ziddiyati va integratsiyasiz ishga tushish) — biz ishga tushirdik (2026-11-02, Node 24.21.0, NestJS 12.2.0,@nestjs/graphql14.0.3, Apollo Server 5.5.1,graphql17.0.2).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!