Passer au contenu

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.

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 :

Exemple d'intercepteur avec des mutations
@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 :

Exemple de transformation du contexte d'exécution
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.

Exemple de filtre d'exception
@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.

Utilisation d'un décorateur personnalisé
export const User = createParamDecorator(
(data: unknown, ctx: ExecutionContext) =>
GqlExecutionContext.create(ctx).getContext().user,
);

Utilisez le décorateur personnalisé @User() comme suit :

Utilisation du décorateur @User
@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 :

Configuration des améliorateurs
GraphQLModule.forRoot({
fieldResolverEnhancers: ['interceptors'],
});
Fonction d'aide pour les résolveurs de champ
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 :

Exemple de classe de pilote personnalisé
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 :

Utilisation du pilote personnalisé
GraphQLModule.forRoot({
driver: ExpressGraphQLDriver,
});