Autres fonctionnalités
Dans le monde de GraphQL, il y a beaucoup de débats sur la gestion des problèmes comme l’authentification ou les effets secondaires des opérations. Devons-nous traiter les choses à l’intérieur de la logique métier ? Devons-nous utiliser une fonction d’ordre supérieur pour améliorer les requêtes et mutations avec la logique d’autorisation ? Ou devrions-nous utiliser les directives de schéma ? Il n’y a pas de réponse unique qui convienne à toutes ces questions.
Nest aide à résoudre ces problèmes avec ses fonctionnalités multiplateformes comme les gardes et les intercepteurs. La philosophie est de réduire la redondance et de fournir des outils qui aident à créer des applications bien structurées, lisibles et cohérentes.
Aperçu
Vous pouvez utiliser les gardes, les intercepteurs, les filtres et les pipes de la même manière avec GraphQL qu’avec n’importe quelle application RESTful. De plus, vous pouvez facilement créer vos propres décorateurs en tirant parti de la fonctionnalité des décorateurs personnalisés. Jetons un coup d’œil à un exemple de gestionnaire de requêtes GraphQL.
@Query('author')@UseGuards(AuthGuard)async getAuthor(@Args('id', ParseIntPipe) id: number) { return this.authorsService.findOneById(id);}
Comme vous pouvez le voir, GraphQL fonctionne avec à la fois les gardes et les pipes de la même manière que les gestionnaires REST HTTP. Grâce à cela, vous pouvez déplacer votre logique d’authentification dans un garde ; vous pouvez même réutiliser la même classe de garde à la fois pour une API REST et GraphQL. De même, les intercepteurs fonctionnent de la même manière sur les deux types d’applications :
@Mutation()@UseInterceptors(EventsInterceptor)async upvotePost(@Args('postId') postId: number) { return this.postsService.upvoteById({ id: postId });}
Contexte d’exécution
Étant donné que GraphQL reçoit un type de données différent dans la requête entrante, le contexte d’exécution reçu par les gardes et intercepteurs est quelque peu différent avec GraphQL par rapport à REST. Les résolveurs GraphQL ont un ensemble distinct d’arguments : root
, args
, context
et info
. Ainsi, les gardes et intercepteurs doivent transformer le ExecutionContext
générique en GqlExecutionContext
. C’est simple :
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';import { GqlExecutionContext } from '@nestjs/graphql';
@Injectable()export class AuthGuard implements CanActivate { canActivate(context: ExecutionContext): boolean { const ctx = GqlExecutionContext.create(context); return true; }}
L’objet de contexte GraphQL renvoyé par GqlExecutionContext.create()
expose une méthode get pour chaque argument de résolveur GraphQL (par exemple, getArgs()
, getContext()
, etc.). Une fois transformé, nous pouvons facilement extraire n’importe quel argument GraphQL pour la requête actuelle.
Filtres d’exception
Les filtres d’exception standard de Nest sont également compatibles avec les applications GraphQL. Comme avec ExecutionContext
, les applications GraphQL devraient transformer l’objet ArgumentsHost
en un objet GqlArgumentsHost
.
@Catch(HttpException)export class HttpExceptionFilter implements GqlExceptionFilter { catch(exception: HttpException, host: ArgumentsHost) { const gqlHost = GqlArgumentsHost.create(host); return exception; }}
Décorateurs personnalisés
Comme mentionné, la fonctionnalité des décorateurs personnalisés fonctionne comme prévu avec les résolveurs GraphQL.
export const User = createParamDecorator( (data: unknown, ctx: ExecutionContext) => GqlExecutionContext.create(ctx).getContext().user,);
Utilisez le décorateur personnalisé @User()
comme suit :
@Mutation()async upvotePost( @User() user: UserEntity, @Args('postId') postId: number,) {}
Exécutez les améliorateurs au niveau du résolveur de champ
Dans le contexte GraphQL, Nest n’exécute pas les améliorateurs (le nom générique pour les intercepteurs, gardes et filtres) au niveau du champ ; ils ne s’exécutent que pour la méthode de niveau supérieur @Query()
/@Mutation()
. Vous pouvez indiquer à Nest d’exécuter des intercepteurs, des gardes ou des filtres pour les méthodes annotées avec @ResolveField()
en définissant l’option fieldResolverEnhancers
dans GqlModuleOptions
. Passez-lui une liste de ‘interceptors’, ‘guards’ et/ou ‘filters’ selon les besoins :
GraphQLModule.forRoot({ fieldResolverEnhancers: ['interceptors'],});
export function isResolvingGraphQLField(context: ExecutionContext): boolean { if (context.getType<GqlContextType>() === 'graphql') { const gqlContext = GqlExecutionContext.create(context); const info = gqlContext.getInfo(); const parentType = info.parentType.name; return parentType !== 'Query' && parentType !== 'Mutation'; } return false;}
Création d’un pilote personnalisé
Nest fournit deux pilotes officiels prêts à l’emploi : @nestjs/apollo
et @nestjs/mercurius
, ainsi qu’une API permettant aux développeurs de créer de nouveaux pilotes personnalisés. Avec un pilote personnalisé, vous pouvez intégrer n’importe quelle bibliothèque GraphQL ou étendre l’intégration existante, en ajoutant des fonctionnalités supplémentaires.
Par exemple, pour intégrer le package express-graphql
, vous pourriez créer la classe de pilote suivante :
import { AbstractGraphQLDriver, GqlModuleOptions } from '@nestjs/graphql';import { graphqlHTTP } from 'express-graphql';
class ExpressGraphQLDriver extends AbstractGraphQLDriver { async start(options: GqlModuleOptions<any>): Promise<void> { options = await this.graphQlFactory.mergeWithSchema(options);
const { httpAdapter } = this.httpAdapterHost; httpAdapter.use( '/graphql', graphqlHTTP({ schema: options.schema, graphiql: true, }), ); }
async stop() {}}
Et ensuite, utilisez-le de la manière suivante :
GraphQLModule.forRoot({ driver: ExpressGraphQLDriver,});