Блог

🛠 Должен ли WordPress иметь GraphQL API в ядре?

Leonardo Losoviz
Автор: Leonardo Losoviz ·

Обновление 01/05/2024: Ознакомьтесь со сравнением Gato GraphQL vs WP REST API.

WordPress 5.7 выходит в ближайшее время. Как и во многих предыдущих релизах, WP REST API также получит несколько новых функций.

Среди новых функций одна привлекла моё внимание: «Image Editor Accepts a List of Modifiers».

Эндпоинт /wp/v2/media/<id>/edit, введённый в WordPress 5.5, поставлялся с ограниченным API, принимавшим объявления поворота и обрезки верхнего уровня. В 50124 этот API стал более мощным и гибким: теперь он принимает массив модификаций в новом параметре запроса 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' } );

Эта разработка велась довольно долго.

Сначала в WordPress 5.5 был представлен эндпоинт редактирования изображений.

Изначально этот эндпоинт был несколько жёстким: требовалось передавать все данные сразу обо всех операциях, применяемых к изображению. Например, чтобы повернуть изображение и изменить его размер, нужно было передать такие данные:

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

Затем в WordPress 5.6 в WP REST API были введены пакетные операции.

Наконец, в предстоящем WordPress 5.7 операции, применяемые к изображению, были разделены, и теперь у нас есть операции "crop" и "rotate". Эти операции можно выполнять по отдельности, а также вместе в одном запросе через batching.

Как видно выше, передача данных в эндпоинт теперь выглядит значительно элегантнее:

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

Воссоздание уже существующего?

WP REST API — не единственный API для WordPress. Существует (как минимум) две альтернативы:

  • GraphQL, через WPGraphQL
  • GraphQL + persisted queries, через Gato GraphQL
    (☝🏽 Это я, ваш хозяин этого блога ☝🏽)

GraphQL — относительно новый тип API, который отлично справляется с пакетными операциями. При использовании GraphQL нет необходимости тратить время и силы на разработку специального решения для них, как это происходит с REST.

Действительно, REST можно считать «копирующим» эту функцию у GraphQL.

REST копирует GraphQL?

Поддержка пакетных операций в WP REST API заняла не менее 2, а возможно, и 3 цикла релизов. Это немалый срок, и он потребовал вклада нескольких людей.

Если бы WordPress также мог использовать GraphQL, а эндпоинт редактирования изображений был основан на GraphQL, а не на REST, то эти участники могли бы работать над другими разработками.

Разве WordPress не выиграл бы и не развивался бы намного быстрее, если бы мог использовать лучшие качества каждого API всякий раз, когда это удобно?

Пакетные операции в GraphQL

Я покажу не один, а несколько способов, которыми Gato GraphQL поддерживает пакетные операции.

Первый — самый простой: добавление нескольких полей в корень запроса. Например, этот запрос выполняет вход пользователя, а затем добавляет комментарий:

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

(Кстати, это клиент GraphiQL. Вот руководство по его использованию.)

Итак, эти две операции применялись к разным объектам, но мы хотим применить несколько операций к одному и тому же объекту.

Давайте сделаем это: этот запрос добавляет два комментария к одной и той же записи.

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

Эти два комментария были добавлены к уже существующей записи. Но что произошло бы, если бы запись также нужно было сначала создать?

В таком случае простой запрос уже не сработает, потому что мы не знаем ID записи, которую ещё предстоит создать, а он нужен в качестве аргумента для других операций (обратите внимание на ? в аргументе поля):

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

Но не отчаивайтесь — Gato GraphQL поможет вам. Он предоставляет не одно, а два решения!

GraphQL API заботится о вас

Первое — использовать функцию выполнения нескольких запросов.

В этом запросе мы выполняем первую операцию, экспортируем её результат через директиву @export, а затем внедряем это значение в качестве входных данных во второй запрос:

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

Ещё элегантнее — использовать вложенные мутации.

В этом запросе мы выполняем первую операцию и вкладываем в неё вторую операцию, так что она будет применена к объекту, созданному в ходе первой операции (а затем повторяем, вкладывая 3-ю операцию, и так далее):

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

В качестве бонуса пакетные операции можно применять не только к одной сущности, но и ко многим сущностям одновременно, в одном запросе.

В этом запросе новые комментарии и все их ответы добавляются к нескольким записям:

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

И у плагина есть ещё один козырь в рукаве: используя функцию встраиваемых полей, можно настраивать содержимое, передаваемое каждому аргументу поля, используя данные самого объекта!

В этом запросе комментарии содержат информацию об объекте, на котором они создаются:

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

Как получить лучшее от REST и GraphQL в нужный момент

По мере развития и расширения Full Site Editing WordPress будет всё больше зависеть от своих API.

Что касается существующих функций, REST API до сих пор справлялся очень хорошо. Нет необходимости перестраивать то, что работает.

Однако что касается новых, ещё не разработанных функций — разве WordPress не выиграл бы от возможности использовать REST или GraphQL в зависимости от того, что удобнее для конкретной функции?

Ответ за вами...

Каково ваше мнение?


Подпишитесь на нашу рассылку

Будьте в курсе всех обновлений Gato GraphQL.