Resolverlar
Resolverlar GraphQL operatsiyasini (query, mutation yoki subscription) ma'lumotga aylantirish bo'yicha ko'rsatmalarni beradi. Ular schemamizda ko'rsatgan shakldagi ma'lumotni - sin
Resolverlar GraphQL operatsiyasini (query, mutation yoki subscription) ma'lumotga aylantirish bo'yicha ko'rsatmalarni beradi. Ular schemamizda ko'rsatgan shakldagi ma'lumotni - sinxron yoki shu shakldagi natijaga yechiladigan promise ko'rinishida - qaytaradi. Odatda siz resolver map ni qo'lda yaratasiz. @nestjs/graphql paketi esa klasslarni annotatsiya qilish uchun ishlatilgan dekoratorlar taqdim etgan metadata asosida resolver map ni avtomatik generatsiya qiladi. Paket imkoniyatlaridan foydalanib GraphQL API yaratish jarayonini ko'rsatish uchun oddiy authors API ni yaratamiz.
Code first
Code first yondashuvida GraphQL schemani qo'lda GraphQL SDL yozish orqali yaratmaymiz. Buning o'rniga, TypeScript dekoratorlaridan foydalanib SDLni TypeScript klass ta'riflaridan generatsiya qilamiz. @nestjs/graphql paketi dekoratorlar orqali berilgan metadatani o'qiydi va schemani avtomatik yaratadi.
Object types
GraphQL schemadagi ko'p ta'riflar object types dan iborat. Har bir object type ilova klienti ishlashi mumkin bo'lgan domen obyektini ifodalashi kerak. Masalan, namunaviy API da authorlar va ularning postlari ro'yxatini olish imkoniyatini berishimiz kerak, shuning uchun bu funksionallikni qo'llab-quvvatlash uchun Author va Post turlarini aniqlashimiz kerak.
Agar schema first yondashuvdan foydalansak, SDLda schemani quyidagicha belgilardik:
1type Author {
2 id: Int!
3 firstName: String
4 lastName: String
5 posts: [Post!]!
6}Bu holatda, code first yondashuvda schemalarni TypeScript klasslari va ularning fieldlarini annotatsiya qiladigan dekoratorlar bilan belgilaymiz. Yuqoridagi SDLning code firstdagi ekvivalenti:
1import { Field, Int, ObjectType } from '@nestjs/graphql';
2import { Post } from './post';
3
4@ObjectType()
5export class Author {
6 @Field(type => Int)
7 id: number;
8
9 @Field({ nullable: true })
10 firstName?: string;
11
12 @Field({ nullable: true })
13 lastName?: string;
14
15 @Field(type => [Post])
16 posts: Post[];
17}TypeScriptning metadata reflection tizimida bir nechta cheklovlar mavjud: masalan, klass qaysi xossalardan iboratligini aniqlash yoki berilgan xossa ixtiyoriy yoki majburiyligini bilish imkonsiz. Shu sababli, schemadagi har bir fieldning GraphQL tipi va optionalitysi haqida metadata berish uchun @Field() dekoratoridan ochiq foydalanishimiz yoki bularni generatsiya qilish uchun CLI plugindan foydalanishimiz kerak.
Author object type ham har qanday klass kabi fieldlar to'plamidan iborat bo'lib, har bir field o'z turini e'lon qiladi. Field turi GraphQL typega mos keladi. Fieldning GraphQL turi boshqa object type yoki scalar type bo'lishi mumkin. GraphQL scalar type - bu ID, String, Boolean, yoki Int kabi bitta qiymatga yechiladigan primitiv tur.
GraphQLning built-in scalar turlaridan tashqari, custom scalar turlarni ham belgilashingiz mumkin (batafsil bu yerda).
Yuqoridagi Author object type ta'rifi SDLda biz ko'rsatgan matnni generatsiya qiladi:
1type Author {
2 id: Int!
3 firstName: String
4 lastName: String
5 posts: [Post!]!
6}@Field() dekoratori ixtiyoriy type funksiyasini (masalan, type => Int) va ixtiyoriy opsiyalar obyektini qabul qiladi.
Type funksiyasi TypeScript tipi va GraphQL tipi o'rtasida noaniqlik bo'lishi mumkin bo'lgan holatlarda kerak bo'ladi. Aniqrog'i: string va boolean turlari uchun kerak emas; number uchun esa kerak (u GraphQL Int yoki Float ga moslashtirilishi kerak). Type funksiyasi shunchaki kerakli GraphQL turini qaytarishi kerak (bu boblarda ko'rsatilgan misollar kabi).
Opsiyalar obyekti quyidagi key/value juftliklarini qabul qilishi mumkin:
nullable: field nullable bo'lishini belgilash (@nestjs/graphqlda har bir field default bo'yicha non-nullable);booleandescription: field tavsifi;stringdeprecationReason: fieldni deprecated deb belgilash;string
Masalan:
1@Field({ description: `Book title`, deprecationReason: 'Not useful in v2 schema' })
2title: string;Butun object type uchun ham tavsif qo'shish yoki deprecate qilish mumkin: @ObjectType({{ '{' }} description: 'Author model' {{ '}' }}).
Field massiv bo'lsa, quyida ko'rsatilgandek, Field() dekoratorining type funksiyasida massiv turini qo'lda ko'rsatishimiz kerak:
1@Field(type => [Post])
2posts: Post[];Array qavs notatsiyasi ([ ]) orqali massiv chuqurligini ko'rsatishimiz mumkin. Masalan, [[Int]] butun sonli matritsani bildiradi.
Massivning o'zi emas, balki uning elementlari nullable bo'lishini belgilash uchun nullable xossasini 'items' qilib qo'ying:
1@Field(type => [Post], { nullable: 'items' })
2posts: Post[];Agar massivning o'zi ham, elementlari ham nullable bo'lsa, nullable ni 'itemsAndList' qilib qo'ying.
Endi Author object type yaratilgach, Post object type ni ham aniqlaymiz.
1import { Field, Int, ObjectType } from '@nestjs/graphql';
2
3@ObjectType()
4export class Post {
5 @Field(type => Int)
6 id: number;
7
8 @Field()
9 title: string;
10
11 @Field(type => Int, { nullable: true })
12 votes?: number;
13}Post object type SDLda quyidagi qismini generatsiya qiladi:
1type Post {
2 id: Int!
3 title: String!
4 votes: Int
5}Code first resolver
Bu nuqtada biz data graphda mavjud bo'lishi mumkin bo'lgan objectlar (type ta'riflari)ni belgiladik, ammo klientlar hali bu objectlar bilan o'zaro ishlay olmaydi. Buni hal qilish uchun resolver klassini yaratishimiz kerak. Code first usulida resolver klassi ham resolver funksiyalarini aniqlaydi, ham Query type ni generatsiya qiladi. Buni quyidagi misolda ko'ramiz:
1@Resolver(() => Author)
2export class AuthorsResolver {
3 constructor(
4 private authorsService: AuthorsService,
5 private postsService: PostsService,
6 ) {}
7
8 @Query(() => Author)
9 async author(@Args('id', { type: () => Int }) id: number) {
10 return this.authorsService.findOneById(id);
11 }
12
13 @ResolveField()
14 async posts(@Parent() author: Author) {
15 const { id } = author;
16 return this.postsService.findAll({ authorId: id });
17 }
18}Barcha dekoratorlar (@Resolver, @ResolveField, @Args va h.k.) @nestjs/graphql paketidan eksport qilinadi.
Bir nechta resolver klassini aniqlashingiz mumkin. Nest ularni runtime da birlashtiradi. Kodni qanday tashkil qilish bo'yicha batafsil ma'lumot uchun quyidagi module bo'limiga qarang.
AuthorsService va PostsService ichidagi mantiq ehtiyojga qarab sodda yoki murakkab bo'lishi mumkin. Bu misolning asosiy maqsadi resolverlarni qanday qurish va ularning boshqa providerlar bilan qanday ishlashini ko'rsatishdir.
Yuqoridagi misolda AuthorsResolver ni yaratdik; u bitta query resolver funksiyasi va bitta field resolver funksiyasini belgilaydi. Resolver yaratish uchun resolver funksiyalari metodlar bo'lgan klass yaratamiz va klassni @Resolver() dekoratori bilan belgilaymiz.
Bu misolda so'rovda yuborilgan idga asoslanib author obyektini olish uchun query handler aniqladik. Metod query handler ekanini ko'rsatish uchun @Query() dekoratoridan foydalaning.
@Resolver() dekoratoriga uzatiladigan argument ixtiyoriy, ammo graf murakkablashganda muhim bo'ladi. U field resolver funksiyalari object graph bo'ylab pastga tushganda foydalanadigan parent obyektni ko'rsatish uchun ishlatiladi.
Bizning misolda klassda field resolver funksiyasi (Author object type ning posts xossasi uchun) mavjud bo'lgani uchun, @Resolver() dekoratoriga ushbu klassda aniqlangan barcha field resolverlar uchun parent type (ya'ni mos ObjectType klassi nomi)ni ko'rsatishimiz shart. Misoldan ko'rinib turibdiki, field resolver yozganda parent obyektga kirish kerak bo'ladi (field shu obyektning a'zosi). Bizning misolda authorning id sini oladigan servisni chaqirib, posts massivini to'ldiramiz. Shuning uchun @Resolver() dekoratorida parent obyektni ko'rsatish zarur. Keyin @Parent() parametr dekoratori orqali field resolver ichida parent obyektga havola olamiz.
Bir nechta @Query() resolver funksiyalarini (shu klass ichida ham, boshqa resolver klasslarida ham) aniqlashingiz mumkin va ular generatsiya qilingan SDLda bitta Query type ta'rifiga hamda resolver mapdagi mos entrylarga birlashtiriladi. Bu querylarni ular foydalanadigan model va servislar yonida belgilash va modullarda tartibli saqlash imkonini beradi.
Nest CLI boilerplate kodning barchasini avtomatik generatsiya qiladigan generator (schematic) taqdim etadi; bu qo'lda qilishimizni oldini oladi va developer tajribasini ancha soddalashtiradi. Bu imkoniyat haqida batafsil bu yerda o'qing.
Query type nomlari
Yuqoridagi misollarda @Query() dekoratori GraphQL schema query type nomini metod nomiga qarab generatsiya qiladi. Masalan, quyidagi konstruktsiyani oling:
1@Query(() => Author)
2async author(@Args('id', { type: () => Int }) id: number) {
3 return this.authorsService.findOneById(id);
4}Bu schemada author query uchun quyidagi entryni generatsiya qiladi (query type metodi bilan bir xil nomdan foydalanadi):
1type Query {
2 author(id: Int!): Author
3}GraphQL querylar haqida ko'proq bu yerda o'qing.
Odatda biz bu nomlarni ajratishni afzal ko'ramiz; masalan, query handler metod nomi getAuthor() bo'lsin, lekin query type nomi author bo'lib qolsin. Xuddi shu narsa field resolverlar uchun ham qo'llanadi. Buni @Query() va @ResolveField() dekoratorlariga mapping nomlarini argument sifatida berib oson qilamiz, quyida ko'rsatilgandek:
1@Resolver(() => Author)
2export class AuthorsResolver {
3 constructor(
4 private authorsService: AuthorsService,
5 private postsService: PostsService,
6 ) {}
7
8 @Query(() => Author, { name: 'author' })
9 async getAuthor(@Args('id', { type: () => Int }) id: number) {
10 return this.authorsService.findOneById(id);
11 }
12
13 @ResolveField('posts', () => [Post])
14 async getPosts(@Parent() author: Author) {
15 const { id } = author;
16 return this.postsService.findAll({ authorId: id });
17 }
18}Yuqoridagi getAuthor handler metodi SDLda quyidagi qismini generatsiya qiladi:
1type Query {
2 author(id: Int!): Author
3}Query dekoratori opsiyalari
@Query() dekoratorining opsiyalar obyektida (yuqorida {{ '{' }}name: 'author'{{ '}' }} bergan joyimizda) quyidagi key/value juftliklari bo'lishi mumkin:
name: query nomi;stringdescription: GraphQL schema hujjatlarida ishlatiladigan tavsif (masalan, GraphQL playgroundda);stringdeprecationReason: queryni deprecated deb belgilash uchun metadata;stringnullable: query null data qaytarishi mumkinligini belgilaydi;booleanyoki'items'yoki'itemsAndList'(yuqorida tushuntirilgan)
Args dekoratori opsiyalari
@Args() dekoratoridan so'rovdagi argumentlarni olish uchun foydalaning. Bu REST route parameter argument extraction bilan juda o'xshash ishlaydi.
Odatda @Args() dekoratori sodda bo'ladi va yuqoridagi getAuthor() misolidagi kabi obyekt argumentini talab qilmaydi. Masalan, identifikator turi string bo'lsa, quyidagi konstruktsiya yetarli va GraphQL so'rovdan nomlangan fieldni olib metod argumenti sifatida beradi.
1@Args('id') id: stringgetAuthor() holatida number tipi ishlatilgan va bu muammo tug'diradi. number TypeScript tipi kutilayotgan GraphQL ko'rinishi haqida yetarli ma'lumot bermaydi (masalan, Int va Float). Shuning uchun type referenceni aniq uzatishimiz kerak. Buni Args() dekoratoriga ikkinchi argument sifatida opsiyalarni berish orqali qilamiz:
1@Query(() => Author, { name: 'author' })
2async getAuthor(@Args('id', { type: () => Int }) id: number) {
3 return this.authorsService.findOneById(id);
4}Opsiyalar obyektida quyidagi ixtiyoriy key/value juftliklarini ko'rsatish mumkin:
type: GraphQL turini qaytaradigan funksiyadefaultValue: default qiymat;anydescription: tavsif metadata;stringdeprecationReason: fieldni deprecate qilish va nega deprecate qilinganini ko'rsatuvchi metadata;stringnullable: field nullable bo'lishi
Query handler metodlari bir nechta argument qabul qilishi mumkin. Faraz qilaylik, authorni firstName va lastName bo'yicha topmoqchimiz. Bu holatda @Args ni ikki marta chaqiramiz:
1getAuthor(
2 @Args('firstName', { nullable: true }) firstName?: string,
3 @Args('lastName', { defaultValue: '' }) lastName?: string,
4) {}firstName GraphQL nullable field bo'lgani uchun, null yoki undefinedning non-value turlarini field tipiga qo'shish shart emas. Biroq, resolverlarda bu non-value turlarni hisobga olgan holda type guard qilish kerakligini yodda tuting, chunki GraphQL nullable field bunday turlarni resolverga o'tkazadi.
Ajratilgan arguments klassi
Inline @Args() chaqiruvlari bilan yuqoridagi kabi kod shishib ketadi. Buning o'rniga alohida GetAuthorArgs arguments klassini yaratib, uni handlerda quyidagicha ishlatishingiz mumkin:
1@Args() args: GetAuthorArgsGetAuthorArgs klassini @ArgsType() yordamida quyidagicha yarating:
1import { MinLength } from 'class-validator';
2import { Field, ArgsType } from '@nestjs/graphql';
3
4@ArgsType()
5class GetAuthorArgs {
6 @Field({ nullable: true })
7 firstName?: string;
8
9 @Field({ defaultValue: '' })
10 @MinLength(3)
11 lastName: string;
12}TypeScript metadata reflection tizimi cheklovlari sababli, tur va optionalityni ko'rsatish uchun @Field dekoratoridan foydalanish yoki CLI plugindan foydalanish kerak. Shuningdek, firstName GraphQL nullable field bo'lgani uchun, null yoki undefinedning non-value turlarini field tipiga qo'shish shart emas. Biroq, resolverlarda bu non-value turlarni hisobga olgan holda type guard qilish kerakligini yodda tuting, chunki GraphQL nullable field bunday turlarni resolverga o'tkazadi.
Bu SDLda quyidagi qismini generatsiya qiladi:
1type Query {
2 author(firstName: String, lastName: String = ''): Author
3}GetAuthorArgs kabi arguments klasslari ValidationPipe bilan juda yaxshi ishlaydi (batafsil bu yerda).
Klass merosi
Standart TypeScript klass merosidan foydalanib, generic utility type funksiyalariga ega (fieldlar, field xossalari, validatsiyalar va h.k.) bazaviy klasslarni yaratish va ularni kengaytirish mumkin. Masalan, sahifalashga oid argumentlar doimo offset va limit fieldlarini o'z ichiga oladi, lekin boshqa indeks fieldlari typega xos bo'lishi mumkin. Quyidagi kabi iyerarxiya qurishingiz mumkin.
Bazaviy @ArgsType() klass:
1@ArgsType()
2class PaginationArgs {
3 @Field(() => Int)
4 offset: number = 0;
5
6 @Field(() => Int)
7 limit: number = 10;
8}Bazaviy @ArgsType() klassdan typega xos subklass:
1@ArgsType()
2class GetAuthorArgs extends PaginationArgs {
3 @Field({ nullable: true })
4 firstName?: string;
5
6 @Field({ defaultValue: '' })
7 @MinLength(3)
8 lastName: string;
9}Xuddi shu yondashuvni @ObjectType() obyektlari bilan ham ishlatish mumkin. Bazaviy klassda generic xossalarni aniqlang:
1@ObjectType()
2class Character {
3 @Field(() => Int)
4 id: number;
5
6 @Field()
7 name: string;
8}Subklasslarda typega xos xossalarni qo'shing:
1@ObjectType()
2class Warrior extends Character {
3 @Field()
4 level: number;
5}Resolver bilan ham merosdan foydalanishingiz mumkin. Merdos va TypeScript generiklarini birlashtirib type safety ta'minlaysiz. Masalan, generic findAll queryga ega bazaviy klass yaratish uchun quyidagi konstruktsiyadan foydalaning:
1function BaseResolver<T extends Type<unknown>>(classRef: T): any {
2 @Resolver({ isAbstract: true })
3 abstract class BaseResolverHost {
4 @Query(() => [classRef], { name: `findAll${classRef.name}` })
5 async findAll(): Promise<T[]> {
6 return [];
7 }
8 }
9 return BaseResolverHost;
10}Quyidagilarni yodda tuting:
- aniq return type (
anyyuqorida) kerak: aks holda TypeScript private klass ta'rifidan foydalanishga shikoyat qiladi. Tavsiya:anyo'rniga interfeys aniqlang. Type@nestjs/commonpaketidan import qilinadiisAbstract: truexossasi ushbu klass uchun SDL generatsiya qilinmasligi kerakligini bildiradi. Eslatma: bu xossani boshqa turlar uchun ham SDL generatsiyasini to'xtatish maqsadida qo'llash mumkin.
Quyidagicha BaseResolver ning konkret subklassini generatsiya qilishingiz mumkin:
1@Resolver(() => Recipe)
2export class RecipesResolver extends BaseResolver(Recipe) {
3 constructor(private recipesService: RecipesService) {
4 super();
5 }
6}Bu konstruktsiya SDLda quyidagini generatsiya qiladi:
1type Query {
2 findAllRecipe: [Recipe!]!
3}Generiklar
Yuqorida generiklardan foydalanishning bir misolini ko'rdik. Bu kuchli TypeScript xususiyati foydali abstraksiyalar yaratishda qo'l keladi. Masalan, quyida shu hujjatga asoslangan kursorli pagination implementatsiyasi keltirilgan:
1import { Field, ObjectType, Int } from '@nestjs/graphql';
2import { Type } from '@nestjs/common';
3
4interface IEdgeType<T> {
5 cursor: string;
6 node: T;
7}
8
9export interface IPaginatedType<T> {
10 edges: IEdgeType<T>[];
11 nodes: T[];
12 totalCount: number;
13 hasNextPage: boolean;
14}
15
16export function Paginated<T>(classRef: Type<T>): Type<IPaginatedType<T>> {
17 @ObjectType(`${classRef.name}Edge`)
18 abstract class EdgeType {
19 @Field(() => String)
20 cursor: string;
21
22 @Field(() => classRef)
23 node: T;
24 }
25
26 @ObjectType({ isAbstract: true })
27 abstract class PaginatedType implements IPaginatedType<T> {
28 @Field(() => [EdgeType], { nullable: true })
29 edges: EdgeType[];
30
31 @Field(() => [classRef], { nullable: true })
32 nodes: T[];
33
34 @Field(() => Int)
35 totalCount: number;
36
37 @Field()
38 hasNextPage: boolean;
39 }
40 return PaginatedType as Type<IPaginatedType<T>>;
41}Yuqoridagi bazaviy klass aniqlangach, endi shu xatti-harakatni meros oladigan maxsus turlarni oson yarata olamiz. Masalan:
1@ObjectType()
2class PaginatedAuthor extends Paginated(Author) {}Schema first
Oldingi bobda aytilganidek, schema first yondashuvda SDLda schema turlarini qo'lda belgilaymiz (batafsil bu yerda). Quyidagi SDL type ta'riflarini ko'rib chiqing.
Ushbu bobda qulaylik uchun barcha SDLni bir joyga (masalan, bitta .graphql faylga) jamladik. Amalda esa kodni modul tarzda tashkil qilish maqsadga muvofiq bo'lishi mumkin. Masalan, har bir domen entitiga mos type ta'riflarini, tegishli servislar, resolver kodi va Nest modul ta'rifi klassi bilan birga alohida direktoriyada saqlash foydali bo'ladi. Nest barcha individual schema type ta'riflarini runtime da birlashtiradi.
1type Author {
2 id: Int!
3 firstName: String
4 lastName: String
5 posts: [Post]
6}
7
8type Post {
9 id: Int!
10 title: String!
11 votes: Int
12}
13
14type Query {
15 author(id: Int!): Author
16}Schema first resolver
Yuqoridagi schema bitta queryni taqdim etadi - author(id: Int!): Author.
GraphQL querylar haqida ko'proq bu yerda o'qing.
Endi author querylarini yechadigan AuthorsResolver klassini yaratamiz:
1@Resolver('Author')
2export class AuthorsResolver {
3 constructor(
4 private authorsService: AuthorsService,
5 private postsService: PostsService,
6 ) {}
7
8 @Query()
9 async author(@Args('id') id: number) {
10 return this.authorsService.findOneById(id);
11 }
12
13 @ResolveField()
14 async posts(@Parent() author) {
15 const { id } = author;
16 return this.postsService.findAll({ authorId: id });
17 }
18}Barcha dekoratorlar (@Resolver, @ResolveField, @Args va h.k.) @nestjs/graphql paketidan eksport qilinadi.
AuthorsService va PostsService ichidagi mantiq ehtiyojga qarab sodda yoki murakkab bo'lishi mumkin. Bu misolning asosiy maqsadi resolverlarni qanday qurish va ularning boshqa providerlar bilan qanday ishlashini ko'rsatishdir.
@Resolver() dekoratori majburiy. U ixtiyoriy string argument qabul qiladi va klass nomini bildiradi. Bu klass nomi, agar klassda @ResolveField() dekoratorlari bo'lsa, Nestga dekorator bilan belgilangan metod qaysi parent type (bizning misolda Author)ga tegishli ekanini bildirish uchun kerak. Muqobil ravishda, @Resolver()ni klass boshida belgilash o'rniga, har bir metodga qo'shish mumkin:
1@Resolver('Author')
2@ResolveField()
3async posts(@Parent() author) {
4 const { id } = author;
5 return this.postsService.findAll({ authorId: id });
6}Bu holatda (@Resolver() metod darajasida bo'lsa), klass ichidagi har bir @ResolveField() uchun @Resolver()ni alohida qo'shishingiz kerak. Bu yaxshi amaliyot hisoblanmaydi (ortiqcha yuk keltiradi).
@Resolver()ga uzatilgan klass nomi argumenti querylar (@Query() dekoratori) yoki mutatsiyalarga (@Mutation() dekoratori) ta'sir qilmaydi.
@Resolver dekoratorini metod darajasida ishlatish code first yondashuvda qo'llab-quvvatlanmaydi.
Yuqoridagi misollarda @Query() va @ResolveField() dekoratorlari GraphQL schema turlariga metod nomi asosida bog'lanadi. Masalan, quyidagi konstruktsiyani oling:
1@Query()
2async author(@Args('id') id: number) {
3 return this.authorsService.findOneById(id);
4}Bu schemada author query uchun quyidagi entryni generatsiya qiladi (query type metodi bilan bir xil nomdan foydalanadi):
1type Query {
2 author(id: Int!): Author
3}Odatda biz bu nomlarni ajratishni afzal ko'ramiz; resolver metodlari uchun getAuthor() yoki getPosts() kabi nomlardan foydalanamiz. Buni dekoratorga mapping nomini argument sifatida berish orqali oson qilamiz, quyida ko'rsatilgandek:
1@Resolver('Author')
2export class AuthorsResolver {
3 constructor(
4 private authorsService: AuthorsService,
5 private postsService: PostsService,
6 ) {}
7
8 @Query('author')
9 async getAuthor(@Args('id') id: number) {
10 return this.authorsService.findOneById(id);
11 }
12
13 @ResolveField('posts')
14 async getPosts(@Parent() author) {
15 const { id } = author;
16 return this.postsService.findAll({ authorId: id });
17 }
18}Nest CLI barcha boilerplate kodni avtomatik generatsiya qiladigan generator (schematic) taqdim etadi va developer tajribasini ancha soddalashtiradi. Bu imkoniyat haqida batafsil bu yerda o'qing.
Typelarni generatsiya qilish
Schema first yondashuvida typings generatsiyasi funksiyasi yoqilgan ( oldingi bobda outputAs: 'class' bilan ko'rsatilganidek) deb faraz qilsak, ilovani ishga tushirgach quyidagi fayl generatsiya qilinadi ( GraphQLModule.forRoot() metodida ko'rsatgan joyga). Masalan, src/graphql.ts:
1export class Author {
2 id: number;
3 firstName?: string;
4 lastName?: string;
5 posts?: Post[];
6}
7export class Post {
8 id: number;
9 title: string;
10 votes?: number;
11}
12
13export abstract class IQuery {
14 abstract author(id: number): Author | Promise<Author>;
15}Klasslarni generatsiya qilish orqali (default interfeys generatsiyasi o'rniga) schema first yondashuv bilan birga deklarativ validatsiya dekoratorlaridan foydalanishingiz mumkin; bu juda foydali yondashuv (batafsil bu yerda). Masalan, generatsiya qilingan CreatePostInput klassiga class-validator dekoratorlarini qo'shib title fieldi uchun minimal va maksimal uzunlikni belgilashingiz mumkin:
1import { MinLength, MaxLength } from 'class-validator';
2
3export class CreatePostInput {
4 @MinLength(3)
5 @MaxLength(50)
6 title: string;
7}Biroq, avtomatik generatsiya qilingan faylga bevosita dekorator qo'shsangiz, bu fayl har safar generatsiya qilinganda ustidan yoziladi. Buning o'rniga, alohida fayl yarating va generatsiya qilingan klassni shunchaki kengaytiring.
1import { MinLength, MaxLength } from 'class-validator';
2import { Post } from '../../graphql.ts';
3
4export class CreatePostInput extends Post {
5 @MinLength(3)
6 @MaxLength(50)
7 title: string;
8}GraphQL argument dekoratorlari
Standart GraphQL resolver argumentlariga maxsus dekoratorlar orqali kirishimiz mumkin. Quyida Nest dekoratorlari va ular ifodalovchi oddiy Apollo parametrlari taqqoslanadi.
@Root() and @Parent() | root/parent |
@Context(param?: string) | context / context[param] |
@Info(param?: string) | info / info[param] |
@Args(param?: string) | args / args[param] |
Bu argumentlarning ma'nosi quyidagicha:
root: parent field resolveridan qaytgan natijani o'z ichiga oladigan obyekt yoki top-levelQueryfieldida server konfiguratsiyasidan berilganrootValuecontext: ma'lum querydagi barcha resolverlar uchun umumiy obyekt; odatda har bir so'rov holatini saqlash uchun ishlatiladiinfo: query bajarilish holati haqida ma'lumotni o'z ichiga oladigan obyektargs: querydagi fieldga uzatilgan argumentlar obyekti
Modul
Yuqoridagi qadamlarni bajarganimizdan so'ng, GraphQLModule resolver mapni generatsiya qilish uchun zarur bo'lgan barcha ma'lumotni deklarativ tarzda belgilab oldik. GraphQLModule dekoratorlar orqali berilgan metadatani reflection yordamida tahlil qiladi va klasslarni to'g'ri resolver mapga avtomatik o'zgartiradi.
Qolgan yagona ish - resolver klass(lar)ini (AuthorsResolver) modulda provider sifatida ko'rsatish va modulni (AuthorsModule) import qilish, shunda Nest undan foydalanadi.
Masalan, buni AuthorsModuleda qilishimiz mumkin; u shu kontekstda kerak bo'ladigan boshqa servislarni ham taqdim etishi mumkin. AuthorsModuleni albatta biror joyda import qiling (masalan, root modulda yoki root modul import qiladigan boshqa modulda).
1@Module({
2 imports: [PostsModule],
3 providers: [AuthorsService, AuthorsResolver],
4})
5export class AuthorsModule {}Kodingizni domen modeli bo'yicha tashkil qilish foydali (REST API dagi entry pointlarni qanday tashkil qilishingizga o'xshash). Bu yondashuvda modellaringizni (ObjectType klasslari), resolverlaringizni va servislaringizni domen modelini ifodalovchi Nest moduli ichida birga saqlang. Har bir modul uchun bularning barchasini bitta papkada saqlang. Shunday qilganingizda va Nest CLI orqali har bir elementni generatsiya qilsangiz, Nest bu qismlarning barchasini avtomatik bog'laydi (fayllarni mos papkalarga joylashtiradi, provider va imports massivlariga yozuvlar qo'shadi va h.k.).