GraphQL4 min read

Boshqa imkoniyatlar

GraphQL dunyosida autentifikatsiya yoki operatsiyalarning side-effectlarini qanday boshqarish haqida ko'p bahslar bor. Buni biznes mantiq ichida qilamizmi? Query va mutationlarni a

GraphQL dunyosida autentifikatsiya yoki operatsiyalarning side-effectlarini qanday boshqarish haqida ko'p bahslar bor. Buni biznes mantiq ichida qilamizmi? Query va mutationlarni avtorizatsiya mantiqi bilan kuchaytirish uchun yuqori darajali funksiyadan foydalanamizmi? Yoki schema directivesdan foydalanamizmi? Bu savollarga yagona, hamma uchun mos javob yo'q.

Nest bu masalalarni guards va interceptors kabi kross-platforma imkoniyatlari bilan hal qilishga yordam beradi. Falsafa - ortiqcha takrorlanishni kamaytirish va tuzilgan, o'qilishi oson hamda izchil ilovalarni yaratishga yordam beradigan vositalarni taqdim etishdir.

Umumiy ko'rinish

GraphQL bilan RESTful ilovalar kabi standart guards, interceptors, filters va pipesdan foydalanishingiz mumkin. Bundan tashqari, custom decorators imkoniyatidan foydalanib o'zingizning dekoratorlaringizni yaratishingiz mumkin. Keling, GraphQL query handler misoliga qaraymiz.

TypeScript
1@Query('author')
2@UseGuards(AuthGuard)
3async getAuthor(@Args('id', ParseIntPipe) id: number) {
4  return this.authorsService.findOneById(id);
5}

Ko'rib turganingizdek, GraphQL ham guards va pipes bilan HTTP REST handlerlari kabi ishlaydi. Shu sababli, autentifikatsiya mantiqini guardga ko'chirishingiz mumkin; hatto xuddi shu guard klassini REST va GraphQL API interfeyslarida qayta ishlatishingiz mumkin. Xuddi shuningdek, interceptorlar ham ikki xil ilova turida bir xil ishlaydi:

TypeScript
1@Mutation()
2@UseInterceptors(EventsInterceptor)
3async upvotePost(@Args('postId') postId: number) {
4  return this.postsService.upvoteById({ id: postId });
5}

Execution context

GraphQL kiruvchi so'rovda boshqa turdagi ma'lumotlarni qabul qilgani uchun, guards va interceptorlar oladigan execution context GraphQLda RESTdan biroz farq qiladi. GraphQL resolverlarda argumentlar to'plami: root, args, context, va info. Shuning uchun guard va interceptorlar umumiy ExecutionContextni GqlExecutionContextga aylantirishi kerak. Bu juda oson:

TypeScript
1import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
2import { GqlExecutionContext } from '@nestjs/graphql';
3
4@Injectable()
5export class AuthGuard implements CanActivate {
6  canActivate(context: ExecutionContext): boolean {
7    const ctx = GqlExecutionContext.create(context);
8    return true;
9  }
10}

GqlExecutionContext.create() qaytargan GraphQL context obyektida har bir GraphQL resolver argumenti uchun get metodi mavjud (masalan, getArgs(), getContext(), va h.k.). Shunday qilib, transformatsiyadan so'ng joriy so'rov uchun istalgan GraphQL argumentni oson ajratib olamiz.

Exception filterlar

Nestning standart exception filterlari GraphQL ilovalari bilan ham mos keladi. ExecutionContextda bo'lgani kabi, GraphQL ilovalari ArgumentsHost obyektini GqlArgumentsHost obyektiga aylantirishi kerak.

TypeScript
1@Catch(HttpException)
2export class HttpExceptionFilter implements GqlExceptionFilter {
3  catch(exception: HttpException, host: ArgumentsHost) {
4    const gqlHost = GqlArgumentsHost.create(host);
5    return exception;
6  }
7}
Hint

GqlExceptionFilter va GqlArgumentsHost@nestjs/graphql paketidan import qilinadi.

RESTdan farqli ravishda, javob yaratish uchun native response obyektidan foydalanilmasligini unutmang.

Custom dekoratorlar

Aytilganidek, custom decorators funksiyasi GraphQL resolverlarda ham kutilganidek ishlaydi.

TypeScript
1export const User = createParamDecorator(
2  (data: unknown, ctx: ExecutionContext) =>
3    GqlExecutionContext.create(ctx).getContext().user,
4);

@User() custom dekoratoridan quyidagicha foydalaning:

TypeScript
1@Mutation()
2async upvotePost(
3  @User() user: UserEntity,
4  @Args('postId') postId: number,
5) {}
Hint

Yuqoridagi misolda user obyektini GraphQL ilovangiz contextiga biriktirilgan deb faraz qilganmiz.

Field resolver darajasida enhancersni ishga tushirish

GraphQL kontekstida Nest field darajasida enhancersni (interceptorlar, guards va filterlar uchun umumiy nom) ishlatmaydi ushbu issuega qarang: ular faqat yuqori darajadagi @Query()/@Mutation() metodlari uchun ishlaydi. @ResolveField() bilan belgilangan metodlar uchun interceptor, guard yoki filterlar ishlashini xohlasangiz, GqlModuleOptionsdagi fieldResolverEnhancers opsiyasini o'rnating. Unga mos ravishda 'interceptors', 'guards' va/yoki 'filters' ro'yxatini bering:

TypeScript
1GraphQLModule.forRoot({
2  fieldResolverEnhancers: ['interceptors']
3}),

Warning Field resolverlar uchun enhancersni yoqish ko'p yozuvlar qaytarilganda va field resolver minglab marta bajarilganda unumdorlik muammolariga olib kelishi mumkin. Shuning uchun fieldResolverEnhancersni yoqqaningizda, field resolverlar uchun qat'iy zarur bo'lmagan enhancersni bajarishni o'tkazib yuborishni tavsiya qilamiz. Buni quyidagi yordamchi funksiya orqali qilishingiz mumkin:

TypeScript
1export function isResolvingGraphQLField(context: ExecutionContext): boolean {
2  if (context.getType<GqlContextType>() === 'graphql') {
3    const gqlContext = GqlExecutionContext.create(context);
4    const info = gqlContext.getInfo();
5    const parentType = info.parentType.name;
6    return parentType !== 'Query' && parentType !== 'Mutation';
7  }
8  return false;
9}

Custom driver yaratish

Nest ikkita rasmiy driverni taqdim etadi: @nestjs/apollo va @nestjs/mercurius, shuningdek yangi custom driverlar yaratish imkonini beradigan API ham mavjud. Custom driver yordamida istalgan GraphQL kutubxonasini integratsiya qilishingiz yoki mavjud integratsiyani kengaytirib, ustiga qo'shimcha imkoniyatlar qo'shishingiz mumkin.

Masalan, express-graphql paketini integratsiya qilish uchun quyidagi driver klassini yaratishingiz mumkin:

TypeScript
1import { AbstractGraphQLDriver, GqlModuleOptions } from '@nestjs/graphql';
2import { graphqlHTTP } from 'express-graphql';
3
4class ExpressGraphQLDriver extends AbstractGraphQLDriver {
5  async start(options: GqlModuleOptions<any>): Promise<void> {
6    options = await this.graphQlFactory.mergeWithSchema(options);
7
8    const { httpAdapter } = this.httpAdapterHost;
9    httpAdapter.use(
10      '/graphql',
11      graphqlHTTP({
12        schema: options.schema,
13        graphiql: true,
14      }),
15    );
16  }
17
18  async stop() {}
19}

So'ng uni quyidagicha ishlating:

TypeScript
1GraphQLModule.forRoot({
2  driver: ExpressGraphQLDriver,
3});