Mutations
La plupart des discussions sur GraphQL se concentrent sur la récupération de données, mais toute plateforme de données complète a besoin d’un moyen de modifier les données côté serveur également. Dans REST, toute demande pourrait entraîner des effets secondaires sur le serveur, mais les meilleures pratiques suggèrent que nous ne devrions pas modifier les données dans les requêtes GET. GraphQL est similaire : techniquement, toute requête pourrait être implémentée pour provoquer une écriture de données. Cependant, comme dans REST, il est recommandé de respecter la convention selon laquelle toutes les opérations qui causent des écritures doivent être envoyées explicitement via une mutation (lisez-en plus ici).
La documentation officielle de Apollo utilise un exemple de mutation upvotePost()
. Cette mutation implémente une méthode pour augmenter la valeur de la propriété votes
d’un post. Pour créer une mutation équivalente dans Nest, nous utiliserons le décorateur @Mutation()
.
Code first
Ajoutons une autre méthode au AuthorResolver
utilisé dans la section précédente (voir les resolvers).
@Mutation(() => Post)async upvotePost( @Args({ name: 'postId', type: () => Int }) postId: number) { return this.postsService.upvoteById({ id: postId });}
Cela générera la partie suivante du schéma GraphQL en SDL :
type Mutation { upvotePost(postId: Int!): Post}
La méthode upvotePost()
prend postId
(Int
) comme argument et renvoie une entité Post
mise à jour. Pour les raisons expliquées dans la section des resolvers, nous devons définir explicitement le type attendu.
Si la mutation doit prendre un objet comme argument, nous pouvons créer un type d’entrée. Le type d’entrée est une sorte spéciale de type d’objet qui peut être passé en argument (lisez-en plus ici). Pour déclarer un type d’entrée, utilisez le décorateur @InputType()
.
import { InputType, Field } from '@nestjs/graphql';
@InputType()export class UpvotePostInput { @Field() postId: number;}
Nous pouvons ensuite utiliser ce type dans la classe resolver :
@Mutation(() => Post)async upvotePost( @Args('upvotePostData') upvotePostData: UpvotePostInput,) {}
Schema first
Étendons notre AuthorResolver
utilisé dans la section précédente (voir les resolvers).
@Mutation()async upvotePost( @Args('postId') postId: number) { return this.postsService.upvoteById({ id: postId });}
Notez que nous avons supposé ci-dessus que la logique métier a été déplacée vers le PostsService
(interroger le post et incrémenter sa propriété votes
). La logique à l’intérieur de la classe PostsService
peut être aussi simple ou sophistiquée que nécessaire. Le principal but de cet exemple est de montrer comment les resolvers peuvent interagir avec d’autres fournisseurs.
La dernière étape consiste à ajouter notre mutation à la définition de types existante.
type Author { id: Int! firstName: String lastName: String posts: [Post]}
type Post { id: Int! title: String votes: Int}
type Query { author(id: Int!): Author}
type Mutation { upvotePost(postId: Int!): Post}
La mutation upvotePost(postId: Int!): Post
est maintenant disponible pour être appelée comme partie de l’API GraphQL de notre application.