Blog

🛠 ¿Debería WordPress tener una API GraphQL en el core?

Leonardo Losoviz
Por Leonardo Losoviz ·

Actualización 01/05/2024: Echa un vistazo a la comparación Gato GraphQL vs WP REST API.

WordPress 5.7 saldrá pronto. Como ha sido durante muchas releases ya, la WP REST API también traerá varias nuevas características.

Entre las nuevas características, una llamó mi atención: "Image Editor Accepts a List of Modifiers".

El endpoint /wp/v2/media/<id>/edit introducido en WordPress 5.5 vino con una API limitada que aceptaba declaraciones de rotación y crop de nivel superior. En 50124 esta API se hizo más potente y flexible aceptando un array de modificaciones en el nuevo parámetro de petición modifiers.

import apiFetch from '@wordpress/api-fetch';
 
const data = {
  modifiers: [
    {
      type: 'crop',
      args: {
        left  : 0,
        top   : 0,
        width : 80,
        height: 80
      }
    },
    {
      type: 'rotate',
      args: {
        angle: 90
      }
    }
  ]
};
apiFetch( { data, method: 'POST', path: '/wp/v2/media/5/edit' } );

Este desarrollo ha tomado un tiempo.

Primero, en WordPress 5.5, se introdujo el endpoint de edición de imágenes.

Este endpoint era inicialmente algo rígido, requiriendo pasar todos los datos juntos sobre todas las operaciones a aplicar sobre la imagen. Por ejemplo, para rotar la imagen, y modificar su tamaño, pasaríamos estos datos:

{
  "x": 0,
  "y": 0,
  "width": 80,
  "height": 80,
  "rotate": 90
}

Luego, en WordPress 5.6, se introdujeron las operaciones en bulk en la WP REST API.

Finalmente, en la próxima WordPress 5.7, las operaciones a aplicar sobre la imagen se han desacoplado, así que tenemos las operaciones "crop" y "rotate". Estas operaciones pueden ejecutarse por sí mismas, pero también juntas en la misma petición vía batching.

Como se vio anteriormente, pasar datos al endpoint ahora se ve mucho más elegante:

{
  "modifiers": [
    {
      "type": "crop",
      "args": {
        "left"  : 0,
        "top"   : 0,
        "width" : 80,
        "height": 80
      }
    },
    {
      "type": "rotate",
      "args": {
        "angle": 90
      }
    }
  ]
}

¿Rehacer lo que ya existe?

La WP REST API no es la única API para WordPress. Hay (al menos) dos alternativas:

  • GraphQL, vía WPGraphQL
  • GraphQL + persisted queries, vía Gato GraphQL
    (☝🏽 Este soy yo, tu anfitrión para esta entrada de blog ☝🏽)

GraphQL es un tipo de API más reciente, que sobresale en operaciones en bulk. Si usas GraphQL, no hay necesidad de gastar tiempo y energía desarrollando una solución personalizada para ellas, como es el caso para REST.

De hecho, podría verse a REST como "copiando" esta característica de GraphQL.

¿REST copiando GraphQL?

Soportar operaciones en bulk en la WP REST API tomó al menos 2, posiblemente 3, ciclos de release para lograr. Esto no es una cantidad insignificante de tiempo, y requirió la contribución de varias personas.

Si WordPress también pudiera hacer uso de GraphQL, y el endpoint de edición de imágenes se basara en GraphQL en lugar de REST, entonces estos contribuidores podrían trabajar en otros desarrollos.

¿No estaría WordPress mejor, y desarrollado mucho más rápido, si pudiera usar las mejores cualidades de cada API, cuando sea conveniente?

Operaciones en bulk en GraphQL

Mostraré no una, sino varias formas en las que Gato GraphQL soporta operaciones en bulk.

La primera es la más simple: añadir varios campos al root de la consulta. Por ejemplo, esta consulta loguea al usuario, y luego añade un comentario:

mutation LogUserInAndAddCommentToPost {
  loginUser(
    by: { credentials: { usernameOrEmail: "test", password: "pass" } }
  ) {
    id
    name
  }
  addCommentToCustomPost(
    input: {
      customPostID: 1459
      commentAs: { html: "Adding a comment: bla bla bla" }
    }
  ) {
    id
    content
    date
  }
}

(Por cierto, este es el cliente GraphiQL. Aquí hay un tutorial sobre su uso.)

Ahora, estas dos operaciones se aplicaron a distintos objetos, pero queremos aplicar varias operaciones al mismo objeto.

Hagamos eso ahora: esta consulta añade dos comentarios a la misma entrada.

mutation AddTwoCommentsToPost {
  firstComment: addCommentToCustomPost(
    input: {
      customPostID: 1459
      commentAs: { html: "This is my first response" }
    }
  ) {
    id
    content
    date
  }
  secondComment: addCommentToCustomPost(
    input: {
      customPostID: 1459
      commentAs: { html: "This is my second response" }
    }
  ) {
    id
    content
    date
  }
}

Estos dos comentarios se añadieron a una entrada ya existente. ¿Pero qué pasaría si la entrada también necesita crearse en primer lugar?

En ese caso, la consulta simple ya no funcionará, porque no sabemos el ID de la entrada aún por crear, que se necesita como argumento para las otras operaciones (fíjate en el ? en el argumento del campo):

mutation CreatePostAndAddTwoCommentsToPost {
  createPost(input: { title: "Some post" }) {
    id  # <= I don't know what this value will be
  }
  addCommentToCustomPost(input: {
    customPostID: ?,
    commentAs: { html: "Blah blah blah" }
  }) {
    id
    content
    date
  }
}

Pero no desesperes, que Gato GraphQL te cubre las espaldas. ¡Proporciona no una, sino dos soluciones!

A Gato GraphQL le importas

La primera es usar la característica Ejecución de múltiples consultas.

En esta consulta, ejecutamos la primera operación, exportamos su resultado vía la directiva @export, y luego inyectamos este valor como entrada a la segunda consulta:

mutation AddComment {
  addCommentToCustomPost(
    customPostID: 1459
    commentAs: { html: "Some insightful comment" }
  ) {
    id @export(as: "newCommentID")
    content
    date
  }
}
 
mutation AddResponseToComment @depends(on: "AddComment") {
  replyComment(
    parentCommentID: $newCommentID
    commentAs: { html: "Debunking your insightful comment" }
  ) {
    id
    date
    content
    parent {
      id
    }
  }
}

Más elegante aún, podemos usar Mutaciones anidadas.

En esta consulta, ejecutamos la primera operación, y anidamos la segunda operación dentro, así que se aplicará al objeto creado durante la primera operación (y luego repetir, anidando una 3ª operación, etc):

mutation AddCommentAndResponseAndResponse {
  addCommentToCustomPost(
    input: {
      customPostID: 1459
      commentAs: { html: "Some insightful comment" }
    }
  ) {
    id
    content
    date
    reply(input: { commentAs: { html: "Debunking your insightful comment" } }) {
      id
      date
      content
      parent {
        id
      }
      reply(input: { commentAs: { html: "No, it was right!" } }) {
        id
        date
        content
        parent {
          id
        }
      }
    }
  }
}

Como bonus, las operaciones en bulk pueden aplicarse no solo a una sola entidad, sino a muchas entidades a la vez, en la misma petición.

En esta consulta, los nuevos comentarios y todas sus respuestas se están añadiendo a varias entradas:

mutation AddCommentAndResponseToManyPosts {
  posts(ids: [1657, 1153, 1499, 1459]) {
    id
    addComment(input: { commentAs: { html: "Some insightful comment" } }) {
      id
      content
      date
      reply(
        input: { commentAs: { html: "Debunking your insightful comment" } }
      ) {
        id
        date
        content
        parent {
          id
        }
      }
    }
  }
}

Y el plugin tiene aún un truco más bajo la manga: usando la característica de campos embebibles, podemos personalizar el contenido pasado a cada argumento de campo, ¡usando datos del propio objeto!

En esta consulta, los comentarios contienen información del objeto sobre el cual se están creando:

mutation AddCustomCommentAndResponseToManyPosts {
  posts(ids: [1657, 1153, 1499, 1459]) {
    id
    addComment(
      input: {
        commentAs: { html: "The post has ID {{ id }} and title {{ title }}" }
      }
    ) {
      id
      content
      date
      reply(
        input: {
          commentAs: {
            html: "The parent comment was posted on {{ dateStr(format: \"d/m/Y\") }}. Cool, right?"
          }
        }
      ) {
        id
        date
        content
        parent {
          id
        }
      }
    }
  }
}

Obteniendo lo mejor de REST y GraphQL cuando sea conveniente

A medida que se desarrolla y expande Full Site Editing, WordPress dependerá cada vez más de sus API(s).

Respecto a las características existentes, la REST API hasta ahora ha funcionado muy bien. No hay necesidad de reconstruir lo que no está roto.

Sin embargo, respecto a nuevas características aún por desarrollar, ¿no se beneficiaría WordPress de usar REST o GraphQL, dependiendo de lo que sea más conveniente para esa característica específica?

La respuesta es tuya...

¿Cuál es tu opinión?


Suscríbete a nuestra newsletter

Mantente al tanto de todas las novedades de Gato GraphQL.