Interagindo com a API GraphQL
Interagindo com a API GraphQLGerenciando payloads de mutations

Gerenciando payloads de mutations

Os campos de mutation podem ser configurados para retornar um destes 2 tipos de entidade distintos:

  • Um tipo de objeto payload
  • Diretamente a entidade modificada

Tipo de objeto payload

Um tipo de objeto payload contém todos os dados relacionados à mutation:

  • O status da mutation (sucesso ou falha)
  • Os erros (se houver) usando tipos GraphQL distintos, ou
  • A entidade modificada com sucesso

Por exemplo, a mutation updatePost retorna um objeto do tipo PostUpdateMutationPayload, e ainda precisamos consultar seu campo post para recuperar a entidade post atualizada:

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
    }
  }
}

O objeto payload nos permite representar melhor os erros, tendo inclusive um tipo GraphQL único para cada tipo de erro. Isso nos permite apresentar reações diferentes para erros diferentes na aplicação, melhorando assim a experiência do usuário.

No exemplo acima, se a operação foi bem-sucedida, receberemos:

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

Se o usuário não estiver autenticado, receberemos:

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

Se o usuário não tiver permissão para editar posts, receberemos:

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

Neste modo, o schema GraphQL conterá muitos tipos adicionais MutationPayload, MutationErrorPayloadUnion e ErrorPayload, portanto terá um tamanho maior:

Schema GraphQL com tipos de objeto payload para mutations

Consultar os objetos payload das mutations

Cada mutation no schema possui um campo correspondente para consultar seus objetos payload criados recentemente, com o nome {mutationName}MutationPayloadObjects.

Esses campos incluem:

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

Esses campos nos permitem recuperar os resultados de mutations executadas com @applyField durante a iteração nos itens de um array.

Por exemplo, a seguinte query duplica posts em massa:

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
    }
  }
}

Por padrão, esses campos não são adicionados ao schema GraphQL. Para isso, devemos selecionar a opção "Use payload types for mutations, and add fields to query those payload objects".

Entidade modificada

A mutation retornará diretamente a entidade modificada em caso de sucesso, ou null em caso de falha, e qualquer mensagem de erro será exibida na entrada de nível superior errors da resposta JSON.

Por exemplo, a mutation updatePost retornará o objeto do tipo Post:

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

Se a operação foi bem-sucedida, receberemos:

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

Em caso de erros, eles aparecerão na entrada errors da resposta. Por exemplo, se o usuário não estiver autenticado, receberemos:

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

Devemos notar que, como resultado, a entrada de nível superior errors conterá não apenas erros de sintaxe, de validação de schema e de lógica (ex.: não passar o nome de um argumento de campo, solicitar um campo inexistente, ou chamar _sendHTTPRequest enquanto a rede está fora, respectivamente), mas também erros de "validação de conteúdo" (ex.: "você não tem autorização para modificar este post").

Como não há tipos adicionais inseridos, o schema GraphQL terá uma aparência mais enxuta:

Schema GraphQL sem tipos de objeto payload para mutations

Gerenciando o tipo de objeto payload para mutations

Vejamos como lidar com a primeira opção, o tipo de objeto payload.

As mutations no schema retornam algum objeto payload, que fornece qualquer erro resultante da mutation, ou o objeto modificado em caso de sucesso (essas 2 propriedades são muito provavelmente exclusivas: ou errors ou object terá um valor, e o outro será null).

Os erros são fornecidos por meio de algum tipo "ErrorPayloadUnion", contendo todos os possíveis erros para aquela mutation. Cada erro possível é algum tipo "ErrorPayload" que implementa a interface ErrorPayload.

Por exemplo, a operação updatePost retorna um PostUpdateMutationPayload, que contém os seguintes campos:

  • status: se a operação foi bem-sucedida ou não, com o valor SUCCESS ou FAILURE
  • post e postID: o objeto post atualizado e seu ID, se a atualização foi bem-sucedida
  • errors: uma lista de CustomPostUpdateMutationErrorPayloadUnion, se a atualização falhou.

O tipo union CustomPostUpdateMutationErrorPayloadUnion contém a lista de todos os possíveis erros que podem ocorrer ao modificar um custom post:

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

O tipo de erro GenericErrorPayload está contido em todos os tipos "ErrorPayloadUnion". Ele é usado sempre que a razão específica do erro não pode ser identificada, como quando wp_update_post simplesmente produz WP_Error. Esse tipo fornece dois campos adicionais: code e data.

Então, para executar a mutation updatePost, podemos executar:

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

Se a operação foi bem-sucedida, receberemos:

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

Se o usuário não estiver autenticado, receberemos:

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

Se o usuário não tiver permissão para editar posts, receberemos:

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