Blog

🛠 O WordPress deveria ter uma API GraphQL no core?

Leonardo Losoviz
Por Leonardo Losoviz ·

Atualização 01/05/2024: Confira a comparação Gato GraphQL vs WP REST API.

O WordPress 5.7 está chegando em breve. Como tem sido em muitos lançamentos, a WP REST API também trará diversas novas funcionalidades.

Entre as novas funcionalidades, uma chamou minha atenção: "Image Editor Accepts a List of Modifiers".

O endpoint /wp/v2/media/<id>/edit introduzido no WordPress 5.5 veio com uma API limitada que aceitava declarações de rotação e recorte de nível superior. Em 50124 essa API foi tornada mais poderosa e flexível ao aceitar um array de modificações no novo parâmetro de requisição 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' } );

Esse desenvolvimento levou algum tempo para ser concretizado.

Primeiro, no WordPress 5.5, foi introduzido o endpoint de edição de imagens.

Esse endpoint era inicialmente um pouco rígido, exigindo que todos os dados referentes a todas as operações a serem aplicadas na imagem fossem passados juntos. Por exemplo, para rotacionar a imagem e modificar seu tamanho, passaríamos estes dados:

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

Depois, no WordPress 5.6, as operações em batch foram introduzidas na WP REST API.

Por fim, no próximo WordPress 5.7, as operações a serem aplicadas na imagem foram desacopladas, resultando nas operações "crop" e "rotate". Essas operações podem ser executadas individualmente, mas também juntas na mesma requisição via batching.

Como visto anteriormente, passar dados para o endpoint agora parece muito mais elegante:

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

Refazer o que já existe?

A WP REST API não é a única API para o WordPress. Existem (pelo menos) duas alternativas:

  • GraphQL, via WPGraphQL
  • GraphQL + queries persistidas, via Gato GraphQL
    (☝🏽 Sou eu, seu anfitrião neste post do blog ☝🏽)

GraphQL é um tipo relativamente novo de API, que se destaca em operações em batch. Ao usar GraphQL, não é preciso gastar tempo e energia desenvolvendo uma solução personalizada para isso, como acontece com REST.

De fato, o REST poderia ser visto como "copiando" essa funcionalidade do GraphQL.

REST copiando GraphQL?

Suportar operações em batch na WP REST API levou pelo menos 2, possivelmente 3, ciclos de lançamento para ser concluído. Não é uma quantidade insignificante de tempo, e exigiu a contribuição de diversas pessoas.

Se o WordPress também pudesse usar GraphQL, e o endpoint de edição de imagens fosse baseado em GraphQL em vez de REST, então esses contribuidores poderiam trabalhar em outros desenvolvimentos.

O WordPress não seria melhor, e não seria desenvolvido muito mais rápido, se pudesse usar as melhores qualidades de cada API, sempre que conveniente?

Operações em batch no GraphQL

Vou mostrar não uma, mas várias formas pelas quais o Gato GraphQL suporta operações em batch.

A primeira é a mais simples: adicionar vários campos à raiz da query. Por exemplo, esta query faz o login do usuário e depois adiciona um comentário:

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

(A propósito, este é o cliente GraphiQL. Aqui está um tutorial sobre como usá-lo.)

Agora, essas duas operações foram aplicadas em objetos diferentes, mas queremos aplicar várias operações no mesmo objeto.

Vamos fazer isso a seguir: esta query adiciona dois comentários ao mesmo post.

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

Esses dois comentários foram adicionados a um post já existente. Mas o que aconteceria se o post também precisasse ser criado primeiro?

Nesse caso, a query simples não funcionará mais, porque não sabemos o ID do post ainda a ser criado, que é necessário como argumento para as outras operações (observe o ? no argumento do 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
  }
}

Mas não se desespere, o Gato GraphQL tem tudo sob controle. Ele oferece não uma, mas duas soluções!

A API GraphQL cuida de você

A primeira é usar a funcionalidade de execução de múltiplas queries.

Nesta query, executamos a primeira operação, exportamos seu resultado via diretiva @export, e então injetamos esse valor como input para a segunda query:

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

Mais elegante ainda, podemos usar as mutations aninhadas.

Nesta query, executamos a primeira operação e aninhamos a segunda operação dentro dela, para que seja aplicada no objeto criado durante a primeira operação (e então repetimos, aninhando uma 3ª operação, e assim por diante):

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 bônus, as operações em batch podem ser aplicadas não apenas em uma única entidade, mas em muitas entidades ao mesmo tempo, na mesma requisição.

Nesta query, os novos comentários e todas as suas respostas estão sendo adicionados a vários posts:

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

E o plugin tem mais um truque na manga: usando a funcionalidade de campos incorporáveis, podemos personalizar o conteúdo passado para cada argumento de campo, usando dados do próprio objeto!

Nesta query, os comentários contêm informações do objeto sobre o qual estão sendo criados:

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

Obter o melhor do REST e do GraphQL sempre que conveniente

À medida que o Full Site Editing é desenvolvido e expandido, o WordPress dependerá cada vez mais de suas APIs.

No que diz respeito às funcionalidades existentes, a REST API se saiu muito bem até agora. Não há necessidade de reconstruir o que não está quebrado.

No entanto, no que diz respeito às novas funcionalidades ainda a serem desenvolvidas, o WordPress não se beneficiaria de usar REST ou GraphQL, dependendo do que for mais conveniente para aquela funcionalidade específica?

A resposta é sua...

Qual é a sua opinião?


Assine nossa newsletter

Fique por dentro de todas as atualizações do Gato GraphQL.