Passer au contenu

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).

Ajouter une méthode upvotePost
@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().

Déclaration d'un type d'entrée pour upvotePost
import { InputType, Field } from '@nestjs/graphql';
@InputType()
export class UpvotePostInput {
@Field()
postId: number;
}

Nous pouvons ensuite utiliser ce type dans la classe resolver :

Utilisation du type d'entrée dans le resolver
@Mutation(() => Post)
async upvotePost(
@Args('upvotePostData') upvotePostData: UpvotePostInput,
) {}

Schema first

Étendons notre AuthorResolver utilisé dans la section précédente (voir les resolvers).

Étendre le AuthorResolver
@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.