Interactuar con la API GraphQL
Interactuar con la API GraphQLManejar los payloads de mutaciones

Manejar los payloads de mutaciones

Los campos de mutación pueden configurarse para devolver cualquiera de estos 2 tipos de entidades distintos:

  • Un tipo de objeto payload
  • Directamente la entidad mutada

Tipo de objeto payload

Un tipo de objeto payload contiene todos los datos relativos a la mutación:

  • El estado de la mutación (éxito o fallo)
  • Los errores (si los hay) usando tipos GraphQL distintivos, o
  • La entidad mutada con éxito

Por ejemplo, la mutación updatePost devuelve un objeto de tipo PostUpdateMutationPayload, y todavía tenemos que consultar su campo post para recuperar la entidad post actualizada:

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

El objeto payload nos permite representar mejor los errores, incluso teniendo un tipo GraphQL único por cada tipo de error. Esto nos permite presentar diferentes reacciones para diferentes errores en la aplicación, mejorando así la experiencia del usuario.

En el ejemplo de arriba, si la operación tuvo éxito, recibiremos:

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

Si el usuario no ha iniciado sesión, recibiremos:

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

Si el usuario no tiene permiso para editar posts, recibiremos:

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

En este modo, el esquema GraphQL contendrá muchos tipos adicionales MutationPayload, MutationErrorPayloadUnion y ErrorPayload, por lo que tendrá un tamaño mayor:

Esquema GraphQL con tipos de objeto payload para mutaciones

Consultar los objetos payload de mutación

Cada mutación en el esquema tiene un campo correspondiente para consultar sus objetos payload recién creados, con nombre {mutationName}MutationPayloadObjects.

Estos campos incluyen:

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

Estos campos nos permiten recuperar los resultados de mutaciones ejecutadas usando @applyField mientras iteramos los elementos de un array.

Por ejemplo, la siguiente consulta duplica posts en bloque:

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 defecto, estos campos no se añaden al esquema GraphQL. Para ello, debemos seleccionar la opción "Use payload types for mutations, and add fields to query those payload objects".

Entidad mutada

La mutación devolverá directamente la entidad mutada en caso de éxito, o null en caso de fallo, y cualquier mensaje de error se mostrará en la entrada de nivel superior errors de la respuesta JSON.

Por ejemplo, la mutación updatePost devolverá el objeto de tipo Post:

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

Si la operación tuvo éxito, recibiremos:

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

En caso de errores, estos aparecerán bajo la entrada errors de la respuesta. Por ejemplo, si el usuario no ha iniciado sesión, recibiremos:

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

Debemos tener en cuenta que, como resultado, la entrada de nivel superior errors contendrá no solo errores de sintaxis, validación del esquema y lógica (p. ej.: no pasar el nombre del argumento de un campo, solicitar un campo inexistente, o llamar a _sendHTTPRequest cuando la red está caída respectivamente), sino también errores de "validación de contenido" (p. ej.: "no estás autorizado a modificar este post").

Como no se añaden tipos adicionales, el esquema GraphQL será más esbelto:

Esquema GraphQL sin tipos de objeto payload para mutaciones

Manejar el tipo de objeto payload para mutaciones

Veamos cómo manejar la primera opción, el tipo de objeto payload.

Las mutaciones en el esquema devuelven algún objeto payload, que proporciona cualquier error resultante de la mutación, o el objeto modificado si tuvo éxito (estas 2 propiedades probablemente son exclusivas: o bien errors o object tendrá un valor, y la otra será null).

Los errores se proporcionan mediante algún tipo "ErrorPayloadUnion", que contiene todos los errores posibles para esa mutación. Cada posible error es algún tipo "ErrorPayload" que implementa la interfaz ErrorPayload.

Por ejemplo, la operación updatePost devuelve un PostUpdateMutationPayload, que contiene los siguientes campos:

  • status: si la operación tuvo éxito o no, con el valor SUCCESS o FAILURE
  • post y postID: el objeto post actualizado y su ID, si la actualización tuvo éxito
  • errors: una lista de CustomPostUpdateMutationErrorPayloadUnion, si la actualización falló.

El tipo unión CustomPostUpdateMutationErrorPayloadUnion contiene la lista de todos los posibles errores que pueden producirse al modificar un custom post:

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

El tipo de error GenericErrorPayload está contenido por todos los tipos "ErrorPayloadUnion". Se usa siempre que no se pueda señalar la razón específica del error, como cuando wp_update_post simplemente produce WP_Error. Este tipo proporciona dos campos adicionales: code y data.

Entonces, para ejecutar la mutación updatePost, podemos ejecutar:

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 la operación tuvo éxito, recibiremos:

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

Si el usuario no ha iniciado sesión, recibiremos:

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

Si el usuario no tiene permiso para editar posts, recibiremos:

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