Interagir avec l'API GraphQL
Interagir avec l'API GraphQLGérer les payloads de mutations

Gérer les payloads de mutations

Les champs de mutation peuvent être configurés pour retourner l'un de ces 2 types d'entités distincts :

  • Un type d'objet payload
  • Directement l'entité mutée

Type d'objet payload

Un type d'objet payload contient toutes les données relatives à la mutation :

  • Le statut de la mutation (succès ou échec)
  • Les erreurs (le cas échéant) en utilisant des types GraphQL distinctifs, ou
  • L'entité mutée avec succès

Par exemple, la mutation updatePost retourne un objet de type PostUpdateMutationPayload, et nous devons encore interroger son champ post pour récupérer l'entité article mise à jour :

mutation UpdatePost {
  updatePost(input: {
    id: 1724,
    title: "New title",
    status: publish
  }) {
    # This is the status of the mutation: SUCCESS or FAILURE
    status
    errors {
      __typename
      ...on ErrorPayload {
        message
      }
    }
    post {
      id
      title
      # This is the status of the post: publish, pending, trash, etc
      status
    }
  }
}

L'objet payload nous permet de mieux représenter les erreurs, en disposant même d'un type GraphQL unique par type d'erreur. Cela nous permet de présenter des réactions différentes pour différentes erreurs dans l'application, améliorant ainsi l'expérience utilisateur.

Dans l'exemple ci-dessus, si l'opération a réussi, nous recevrons :

{
  "data": {
    "updatePost": {
      "status": "SUCCESS",
      "errors": null,
      "post": {
        "id": 1724,
        "title": "Some title",
        "status": "publish"
      }
    }
  }
}

Si l'utilisateur n'est pas connecté, nous recevrons :

{
  "data": {
    "updatePost": {
      "status": "FAILURE",
      "errors": [
        {
          "__typename": "UserIsNotLoggedInErrorPayload",
          "message": "You must be logged in to create or update custom posts"
        }
      ],
      "post": null
    }
  }
}

Si l'utilisateur n'a pas la permission de modifier des articles, nous recevrons :

{
  "data": {
    "updatePost": {
      "status": "FAILURE",
      "errors": [
        {
          "__typename": "LoggedInUserHasNoEditingCustomPostCapabilityErrorPayload",
          "message": "Your user doesn't have permission for editing custom posts."
        }
      ],
      "post": null
    }
  }
}

Dans ce mode, le schéma GraphQL contiendra de nombreux types supplémentaires MutationPayload, MutationErrorPayloadUnion et ErrorPayload, ce qui lui donnera une taille plus importante :

Schéma GraphQL avec types d'objet payload pour les mutations

Interroger les objets payload de mutation

Chaque mutation dans le schéma dispose d'un champ correspondant pour interroger ses objets payload récemment créés, avec le nom {mutationName}MutationPayloadObjects.

Ces champs comprennent :

  • addCommentToCustomPostMutationPayloadObjects (pour addCommentToCustomPost)
  • createCustomPostMutationPayloadObjects (pour createCustomPost)
  • createMediaItemMutationPayloadObjects (pour createMediaItem)
  • createPageMutationPayloadObjects (pour createPage)
  • createPostMutationPayloadObjects (pour createPost)
  • removeFeaturedImageFromCustomPostMutationPayloadObjects (pour removeFeaturedImageFromCustomPost)
  • replyCommentMutationPayloadObjects (pour replyComment)
  • setCategoriesOnPostMutationPayloadObjects (pour setCategoriesOnPost)
  • setFeaturedImageOnCustomPostMutationPayloadObjects (pour setFeaturedImageOnCustomPost)
  • setTagsOnPostMutationPayloadObjects (pour setTagsOnPost)
  • updateCustomPostMutationPayloadObjects (pour updateCustomPost)
  • updatePageMutationPayloadObjects (pour updatePage)
  • updatePostMutationPayloadObjects (pour updatePost)

Ces champs nous permettent de récupérer les résultats de mutations exécutées avec @applyField lors de l'itération sur les éléments d'un tableau.

Par exemple, la requête suivante duplique des articles en masse :

query GetPostsAndExportData
{
  postsToDuplicate: posts {
    title
    rawContent
    excerpt
 
    # Already create (and export) the inputs for the mutation
    postInput: _echo(value: {
      title: $__title
      contentAs: {
        html: $__rawContent
      },
      excerpt: $__excerpt
    })
      @export(as: "postInput", type: LIST)
      @remove
  }
}
 
mutation CreatePosts
  @depends(on: "GetPostsAndExportData")
{
  createdPostMutationPayloadObjectIDs: _echo(value: $postInput)
    @underEachArrayItem(
      passValueOnwardsAs: "input"
    )
      @applyField(
        name: "createPost"
        arguments: {
          input: $input
        },
        setResultInResponse: true
      )
    @export(as: "createdPostMutationPayloadObjectIDs")
}
 
query DuplicatePosts
  @depends(on: "CreatePosts")
{
  createdPostMutationObjectPayloads: createPostMutationPayloadObjects(input: {
    ids: $createdPostMutationPayloadObjectIDs
  }) {
    status
    errors {
      __typename
      ...on ErrorPayload {
        message
      }
    }
    post {
      id
      title
      rawContent
      excerpt
    }
  }
}

Par défaut, ces champs ne sont pas ajoutés au schéma GraphQL. Pour cela, nous devons sélectionner l'option "Use payload types for mutations, and add fields to query those payload objects".

Entité mutée

La mutation retournera directement l'entité mutée en cas de succès, ou null en cas d'échec, et tout message d'erreur sera affiché dans l'entrée de niveau supérieur errors de la réponse JSON.

Par exemple, la mutation updatePost retournera l'objet de type Post :

mutation UpdatePost {
  updatePost(input: {
    id: 1724,
    title: "New title",
    status: publish
  }) {
    id
    title
    status
  }
}

Si l'opération a réussi, nous recevrons :

{
  "data": {
    "updatePost": {
      "id": 1724,
      "title": "Some title",
      "status": "publish"
    }
  }
}

En cas d'erreurs, celles-ci apparaîtront sous l'entrée errors de la réponse. Par exemple, si l'utilisateur n'est pas connecté, nous recevrons :

{
    "errors": [
      {
        "message": "You must be logged in to create or update custom posts'",
        "locations": [
          {
            "line": 2,
            "column": 3
          }
        ]
      }
  ],
  "data": {
    "updatePost": null
  }
}

Nous devons noter que, par conséquent, l'entrée de niveau supérieur errors contiendra non seulement des erreurs de syntaxe, de validation du schéma et de logique (ex. : ne pas passer le nom d'un argument de champ, demander un champ inexistant, ou appeler _sendHTTPRequest alors que le réseau est hors ligne respectivement), mais aussi des erreurs de « validation de contenu » (ex. : « vous n'êtes pas autorisé à modifier cet article »).

Comme aucun type supplémentaire n'est ajouté, le schéma GraphQL sera plus épuré :

Schéma GraphQL sans types d'objet payload pour les mutations

Gérer le type d'objet payload pour les mutations

Voyons comment gérer la première option, le type d'objet payload.

Les mutations dans le schéma retournent un objet payload, qui fournit toute erreur résultant de la mutation, ou l'objet modifié en cas de succès (ces 2 propriétés sont très probablement exclusives : soit errors soit object aura une valeur, et l'autre sera null).

Les erreurs sont fournies via un type « ErrorPayloadUnion », contenant toutes les erreurs possibles pour cette mutation. Chaque erreur possible est un type « ErrorPayload » qui implémente l'interface ErrorPayload.

Par exemple, l'opération updatePost retourne un PostUpdateMutationPayload, qui contient les champs suivants :

  • status : si l'opération a réussi ou non, avec la valeur SUCCESS ou FAILURE
  • post et postID : l'objet article mis à jour et son identifiant, si la mise à jour a réussi
  • errors : une liste de CustomPostUpdateMutationErrorPayloadUnion, si la mise à jour a échoué.

Le type union CustomPostUpdateMutationErrorPayloadUnion contient la liste de toutes les erreurs possibles pouvant survenir lors de la modification d'un custom post :

  • CustomPostDoesNotExistErrorPayload
  • GenericErrorPayload
  • LoggedInUserHasNoEditingCustomPostCapabilityErrorPayload
  • LoggedInUserHasNoPermissionToEditCustomPostErrorPayload
  • LoggedInUserHasNoPublishingCustomPostCapabilityErrorPayload
  • UserIsNotLoggedInErrorPayload

Le type d'erreur GenericErrorPayload est contenu par tous les types « ErrorPayloadUnion ». Il est utilisé lorsque la raison spécifique de l'erreur ne peut pas être identifiée, comme lorsque wp_update_post produit simplement WP_Error. Ce type fournit deux champs supplémentaires : code et data.

Ainsi, pour exécuter la mutation updatePost, nous pouvons exécuter :

mutation UpdatePost(
  $postId: ID!
  $title: String!
) {
  updatePost(
    input: {
      id: $postId,
      title: $title,
    }
  ) {
    status
    errors {
      __typename
      ...on ErrorPayload {
        message
      }
      ...on GenericErrorPayload {
        code
      }
    }
    post {
      id
      title
    }
  }
}

Si l'opération a réussi, nous recevrons :

{
  "data": {
    "updatePost": {
      "status": "SUCCESS",
      "errors": null,
      "post": {
        "id": 1724,
        "title": "This incredible title"
      }
    }
  }
}

Si l'utilisateur n'est pas connecté, nous recevrons :

{
  "data": {
    "updatePost": {
      "status": "FAILURE",
      "errors": [
        {
          "__typename": "UserIsNotLoggedInErrorPayload",
          "message": "You must be logged in to create or update custom posts"
        }
      ],
      "post": null
    }
  }
}

Si l'utilisateur n'a pas la permission de modifier des articles, nous recevrons :

{
  "data": {
    "updatePost": {
      "status": "FAILURE",
      "errors": [
        {
          "__typename": "LoggedInUserHasNoEditingCustomPostCapabilityErrorPayload",
          "message": "Your user doesn't have permission for editing custom posts."
        }
      ],
      "post": null
    }
  }
}