Всем привет! Меня зовут Александр, я фронтенд-разработчик в KTS.

Не так давно я уже рассказывал про Strapi — одно из ведущих опенсорсных headless CMS‑решений, которое на протяжении долгого времени пользуется большой популярностью у разработчиков. Чуть больше месяца назад разработчики системы представили обновленную версию, и в этой статье я расскажу о фишках, которые появились в Strapi 5.

Неважно, переходите вы на Strapi 5 с предыдущей версии или только начинаете знакомиться с технологией — понимание нововведений в любом случае пойдет вам на пользу, поскольку без них вы не сможете полностью раскрыть потенциал актуальной версии. В этой статье перечислены 10 нововведений в Strapi, о которых вам стоит знать.

За основу материала я взял информацию с официального сайта Strapi об обновлениях для разработчиков и для контент‑менеджеров, поэтому некоторые скриншоты делал не сам, а скромно переиспользовал. Однако в своем обзоре я разберу основные фишки на более детальных примерах, а также расскажу об особенностях, не упомянутых в рекламных статьях. Приступим.

Оглавление

  1. Новый формат ответа API

  2. Document Service API вместо Entity Service API

    1. Использование в контроллерах коллекций

    2. documentId вместо id

    3. Функциональность Draft&Publish

      1. Отдельные вкладки Draft и Published

      2. Массовые операции

    4. Работа с многоязычным контентом

  3. Обновления TypeScript

    1. Система типов в Strapi 5

    2. Миграция кода в TypeScript

    3. Операции с данными

  4. Создание записи при загрузке файла

  5. Новые функции и параметр запроса

    1. Статус публикации

    2. Функция count()

    3. Функция publish()

  6. Plugin SDK

  7. Vite – инструмент сборки по умолчанию

  8. Чат-бот с ИИ

  9. LaunchPad – обновленная витрина

  10. Платные функции

    1. Content History

    2. Расписание публикаций

  11. Заключение

1. Новый формат ответа API

Работа с API стала проще и понятнее — теперь экземпляры коллекций (а также их внутренние связи) больше не оборачиваются в структуру вида { id, attributes }.

Сравним со Strapi 4. Раньше запрашиваемые данные оборачивались в ключ attributes, и ответ API выглядел следующим образом:

// Ответ API в Strapi v4:
{
  "data": {
    // поля системы
    "id": 14,
    "attributes": {
      // пользовательские поля
      "title": "Article A",
      "relation": {
        "data": {
          "id": "clkgylw7d000108lc4rw1bb6s",
          "attributes": {
            "name": "Category A"
          }
        }
      }
    }
  },
  "meta": {
    "pagination": {
      "page": 1,
      "pageSize": 10
    }
  }
}

В Strapi 5 Контент-API возвращает экземпляры коллекции, не оборачивая их в объект attributes:

// Ответ API в Strapi v5:
{
  "data": {
    // поля системы
    "documentId": "clkgylmcc000008lcdd868feh",
    "locale": "en",
    // пользовательские поля
    "title": "Article A",
    "relation": {
      // поля системы
      "documentId": "clkgylw7d000108lc4rw1bb6s",
      // пользовательские поля
      "name": "Category A"
    }
  },
  "meta": {
    "pagination": {
      "page": 1,
      "pageSize": 10
    }
  }
}

Обратите внимание на новый параметр documentId — о нем мы поговорим чуть позже.

Если же вам потребуется ответ старого формата при работе со Strapi 5, вы все равно можете его получить. Для этого в заголовке к запросу нужно задать параметр Strapi-Response-Format: v4.

Пример

Попробуем извлечь запись в Strapi 5:

Strapi 5 Response without Strapi 4 Headers.png
Получение статьи по documentId в Strapi 5 без дополнительных заголовков

Как видите, в ответе нет объекта attributes, поскольку в заголовке не задан параметр Strapi-Response-Format: v4. Давайте зададим его и сравним:

Strapi 5 with v4 Response format.png
Получение статьи по documentId в Strapi 5 c заголовком Strapi-Response-Format: v4

С заголовком мы получаем ответ API в старом формате.

2. Document Service API вместо Entity Service API

В Strapi 5 для работы с коллекциями на замену Entity Service API приходит Document Service API. Новый API-сервис позволяет расширить функциональность для коллекций:

  • Draft and Publish каждый элемент коллекции теперь одновременно имеет версии черновика и опубликованного варианта (в интерфейсе Strapi — вкладки Draft и Publish). В Strapi 4 черновик у элемента существовал только до его первой публикации;

  • Content History: можно отслеживать изменение черновика и опубликованного варианта и откатываться к предыдущим версиям;

  • локализация: благодаря переходу от id к documentId (подробнее расскажем ниже) каждая сущность теперь может существовать в различных локалях. Ранее элементы в локалях существовали независимо друг от друга, и для публикации записи на нескольких языках приходилось ее дублировать.

    Устройство контента в Strapi 5: теперь каждый элемент коллекции имеет версии draft/publish и может быть представлен в нескольких локалях
    Устройство контента в Strapi 5: теперь каждый элемент коллекции имеет версии draft/publish и может быть представлен в нескольких локалях

Раньше все вышеописанные действия требовали значительных усилий. Например, между таблицами приходилось настраивать дополнительные связи, из-за которых падала скорость запросов, а при локализации требовалось дублировать записи для разных локалей.

2.1. Использование в контроллерах коллекций

Предлагаю рассмотреть подробнее Entity Service API и Document Service API в контроллерах различных коллекций (более подробно об кастомизации контроллеров вы можете прочитать в нашей предыдущей статье).

Сравним формат запроса в Entity Service API и Document Service API:

  • Entity API:

    strapi.entityService.findMany(uid, {
      fields: ["id", "name", "description"],
      populate: ["author", "comments"],
      publicationState: "live",
    });
  • Document API:

    strapi.documents(uid).findMany({
      fields: ["id", "name", "description"],
      populate: ["author", "comments"],
      status: "published",
    });

В обоих случаях мы получаем только опубликованные записи, просто с помощью разных API. Поддержка Entity Service API в Strapi 5 все еще присутствует, но ее использовать не рекомендуется — в будущих версиях планируется оставить только Document Service API. Подробнее об отличиях в запросах с помощь Entity Service API и Document Service API можно почитать в документации Strapi.

У Document Service API и Entity Service API очень похожий синтаксис, поэтому опытным Strapi-разработчикам не составит труда освоить новый инструмент. Тем не менее, в Strapi 5 появляются новые функции — их мы рассмотрим ниже.

2.2. documentId вместо id

Из-за особенностей Entity Service API в четвертой версии Strapi приходилось использовать id записи при каждом обращении к API. При поиске записи по ее id можно было получить следующий ответ:

fetch entry by id.png
Получение записи по ее id

В свою очередь, в Strapi 5 появилась возможность делать запросы к API по id документа (параметр documentId):

fetch entry by documentid.png
Получение записи по ее documentId

Заметьте, что id записи также присутствует в ответе, и по его значению все еще можно обращаться к API сервиса Document.

2.3. Функциональность Draft&Publish

Разработка и публикация контента всегда подразумевают довольно сложный и запутанный процесс. Функциональность Draft&Publish значительно упрощает его и дает новые возможности по контролю публикаций.

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

В Strapi 5 черновая и опубликованная версии контента могут существовать независимо друг от друга. В интерфейсе они разделены на две вкладки — Draft и Published.

Изменения во вкладке Draft можно сохранять, что позволяет нескольким людям вносить последовательные правки в контент перед публикацией. Более того, при работе над черновиком контент‑менеджеры больше не рискуют случайно выкатить сырой материал на всеобщее обозрение или потерять прогресс из‑за того, что забыли отправить его в Published.

Каждая запись теперь пребывает в одном из трех статусов:

  • Draft — контент никогда не публиковался и пока в работе;

  • Modified — контент публиковался ранее, но с момента последней публикации в его черновую версию были внесены изменения;

  • Published — контент опубликован, и в черновой версии нет новых правок.

Статус виден в режиме Content Manager в верхней части записи (подробнее о режимах мы также рассказывали тут). Таким образом, при командной работе над материалом контент-менеджерам не придется гадать, вносил ли кто-то из их коллег изменения после последней публикации.

Демонстрация статусов контента: published (опубликован) и modified (изменялся после публикации)
Демонстрация статусов контента: published (опубликован) и modified (изменялся после публикации)

2.3.1. Отдельные вкладки Draft и Published

В обновленном интерфейсе черновой и опубликованный контент размещены на отдельных вкладках:

  • Draft — рабочее пространство, в котором можно вносить и сохранять изменения до тех пор, пока они не будут готовы к публикации;

  • Published — вкладка, на которой отображается опубликованный контент. На этой вкладке его редактировать нельзя, она нужна для того, чтобы вы понимали, какой материал пользователи видят на вашем сайте.

Между этими вкладками можно свободно переключаться, чтобы сравнивать черновик с опубликованной версией. Довольно полезная фишка, которая поможет не стереть важную информацию по неосторожности.

Элемент Article со статусом Modified во вкладке Draft (после последней публикации был изменен и сохранен, но еще не опубликован заново)
Элемент Article со статусом Modified во вкладке Draft (после последней публикации был изменен и сохранен, но еще не опубликован заново)

При работе с черновиком во вкладке Draft доступны следующие действия:

  • Publish — публикация черновика;

  • Save — сохранение черновика без публикации;

  • Discard changes — отмена внесенных изменений и откат к последней сохраненной версии черновика.

Также можно отменить публикацию контента. Если нужно будет скрыть с сайта какую‑то запись, контент‑менеджер сможет удалить опубликованную версию, и контент вернется в статус Draft. После этого доработать материал можно будет доработать перед повторной публикацией или удалить его окончательно.

Подробнее с функциональностью Draft&Publish можно ознакомиться в официальной документации Strapi.

2.3.2. Массовые операции

В Strapi 5 появляется возможность параллельно управлять статусами нескольких записей. Массовые операции в режиме Content Manager позволяют выделить необходимые записи и опубликовать их одновременно. Аналогичным образом можно удалять несколько опубликованных записей сразу.

Если вы локализуете контент под разные регионы, обратите внимание — Internationalization в Strapi 5 реализована как элемент базовой функциональности, а не как отдельный плагин. Благодаря этому вы сможете точечно выполнять массовые операции с контентом в конкретной локализации.

2.4. Работа с многоязычным контентом

В Strapi 5 подход к локалям изменился — благодаря documentId все переводы и локализованные версии контента теперь совмещены в один «документ».

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

Давайте посмотрим, как это выглядит в новой версии Strapi. Для примера возьмем статью на английском языке:

Элемент коллекции Article в английской локали
Элемент коллекции Article в английской локали

При запросе на список статей мы увидим ее в списке с ”documentId”: “cr2j378mpnjq6o95sogelzns” (если при запросе не указывать параметр locale, то его значение задается по умолчанию — в нашем случае это en):

Получение элемента коллекции в ее локали по умолчанию (en)
Получение элемента коллекции в ее локали по умолчанию (en)

Теперь создадим версию этой статьи для русской локали. Для этого в режиме Content Manager выберем элемент коллекции и справа вверху сменим в нем локаль, а затем заполним ее контентом на русском языке:

Элемент коллекции Article в русской локали
Элемент коллекции Article в русской локали

А затем сделаем еще один запрос на список статей, но на этот раз – с параметром “locale”: “ru”:

Получение элемента коллекции в русской локали
Получение элемента коллекции в русской локали

Как мы видим, documentId не изменился — это все тот же элемент в Strapi, но с другим контентом для другой локали.

3. Обновления TypeScript

Поддержка TypeScript была еще в Strapi 4. Тогда же разработчики добавили экспериментальную типизированную систему для API, а вместе с ней и автоматизированную генерацию типов контента и компонентов. Однако в Strapi 5 появляются еще несколько нововведений.

3.1. Система типов в Strapi 5

В новую версию CMS добавлена возможность импортировать необходимые неймспейсы, используя типы для API. У каждого типа в системе есть описание и пример использования.

Теперь все js-файлы при создании проекта становятся ts-файлами:

Структура проекта – теперь вместо js-файлов используются ts-файлы
Структура проекта – теперь вместо js-файлов используются ts-файлы

А сам Strapi теперь позволяет использовать свои системные типы. Они могут пригодиться, например, при типизации возвращаемых данных в контроллерах:

import type {Data, Schema, UID, Utils} from "@strapi/strapi";
// ...
declare function fetch<T extends UID.Schema>(uid: T) : Data.Entity<T>;
declare function getComponentSchemas(): Utils.String.Dict<Schema.Component>;

3.2. Миграция кода в TypeScript

Изначально Strapi был полностью написан на JavaScript, однако к пятой версии код почти целиком переехал на TypeScript. Отчасти это сказывается на удобстве работы с ориентированными на пользователя API.

Strapi TypeScript Migration on Github.png
Использованные языки в Strapi 5 (теперь преобладает TS)

3.3. Операции с данными

В новой версии обеспечена полная совместимость с TypeScript, следовательно, при работе с системой не придется гадать, какие методы, фильтры и типы данных поддерживаются, а какие нет.

Strapi Data Manipulation with TypeScript.png
Демонстрация автокомплита в ts-файлах Strapi 5

4. Создание записи при загрузке файла

К сожалению, без минусов не обошлось — создавать записи при загрузке файлов стало менее удобно. Раньше система поддерживала автоматическое создание записи при таком сценарии. Теперь в качестве альтернативы можно пользоваться определенной последовательностью действий.

  1. Сначала нужно выгрузить файл и получить его id — этот же id будет использоваться в записи.

    Как получить id файла

    Выгружаем файл:

    const file_upload_response = await
    fetch(`${StrapiURL}/api/upload`, {
      method: 'post',
      body: formData
    });

    Получаем id выгруженного файла:

    const id = file_upload_response[0].id

  2. Когда id файла получен, нужно использовать его при создании записи.

    Как создать запись с id файла
    const newEntry = {
      data: {
        entryName: data.name,
        entryFile: id // file id
      }
    }

    Делаем запрос к API на создание новой записи:

    const response = await fetch(`${StrapiURL}/api/{collection}`, {
      method: 'post',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(newEntry),
    });

5. Новые функции и параметр запроса

С появлением Draft&Publish в Document Service API добавился новый параметр запроса status, а также несколько новых функций:

  • count();

  • publish();

  • unpublish();

  • discardDraft().

Ниже рассмотрим некоторые из них.

5.1. Статус публикации

GET-запросы для извлечения записи обзавелись параметром status. С его помощью можно выбирать статус записи, которую вернет API — для этого параметру нужно задать значение published или draft.

В качестве примера возьмем запись, у которой есть и опубликованная, и черновая версии:

Вкладка Draft у публикации
Вкладка Draft у публикации
Strapi published entry.png
Вкладка Published у публикации

Если не указывать параметр status, по умолчанию API возвращает опубликованную версию.

5.2. Функция count()

Функция count() позволяет извлечь определенное количество документов, соответствующих заданным параметрам.

5.3. Функция publish()

С помощью функции publish() можно опубликовать документ или несколько его локализованных версий. Он работает только в том случае, если включена функциональность Draft&Publish.

Разберемся на примере, как это работает. Предположим, что у нас есть статья на английском языке и черновик ее локализации на французском. Французский вариант нам нужно опубликовать.

Примерно так мог выглядеть наш переписанный контроллер, который публиковал бы статью сразу в несколько локалей:

// src/api/article/controllers/article.js

const { createCoreController } = require('@strapi/strapi').factories;

module.exports = createCoreController('api::article.article', ({ strapi }) => ({
  async create(ctx) {
    // Создаем основную статью, например, в английской локали
    const response = await super.create(ctx);
    const article = response.data;
    
    // Проверяем, что статья создана успешно
    if (article) {
      // Публикуем статью во французской локали (или других локалях)
      await strapi.documents('api::article.article').publish({
        documentId: article.documentId,
        locale: 'fr',  // Измените на нужную локаль
      });
      
      // Можно добавить публикацию в дополнительных локалях, если требуется
      await strapi.documents('api::article.article').publish({
        documentId: article.documentId,
        locale: 'es', // Пример для испанского варианта
      });
    }

    return response;
  },
}));

Готово.

6. Plugin SDK

Для командной строки также появляется новый инструмент — Plugin SDK. С его помощью можно создавать плагины и добавлять их в проекты Strapi уже после того, как эти проекты были заведены.

Чтобы создать Strapi-плагин, нужно выполнить следующие действия:

  1. Создать сам плагин (процесс создания подробно описан в документации).

  2. Подлинковать плагин к проекту — для это го нужно выполнить следующие команды:
    cd /path/to/strapi/project
    npx yalc add --link my-strapi-plugin && npm install
    где my-strapi-plugin – имя вашего плагина.
    Внимание: перед тем, как выполнять вторую команду, проверьте, что вы действительно перешли в ваш Strapi-проект, а также убедитесь, что yalk установлен и работает глобально.

  3. Опубликовать плагин – для этого нужно выполнить следующую команду:
    npm run build && npm run verify
    Данная команда проверяет, готов ли плагин к публикации, и завершает процесс его создания.

На случай, если вам понадобится детальнее разобраться в тонкостях работы с Plugin SDK, оставлю ссылку на документацию.

7. Vite — инструмент сборки по умолчанию

Раньше вStrapi использовался Webpack — опенсорсный модульный инструмент сборки для JavaScript. С новой версией системы разработчики решили перейти на Vite по следующим соображениям:

  • Vite обеспечивает более высокую скорость работы в Dev‑режиме благодаря Go‑модулю esbuild — он позволяет собирать код на 15% быстрее, чем JavaScript-модули;

  • Vite не требует конфигурационных файлов для сборки кода.

Strapi 5 uses Vite.png
Теперь Strapi использует Vite

8. Чат-бот с ИИ

Документация Strapi, на которую я уже неоднократно ссылался, обзавелась собственным чат-ботом с ИИ (диалоговое окно открывается по кнопке Ask AI в правом верхнем углу страницы). Теперь можно задать ему интересующий вопрос и получить быстрый ответ. Несмотря на удобство, слепо верить боту яне рекомендую — его ответы лучше на всякий случай перепроверять.

Взаимодействие с чат-ботом
Взаимодействие с чат-ботом

9. LaunchPad — обновленная витрина

На смену FoodAdvisor — предыдущему приложению на Strapi, предназначенному для демонстрации возможностей системы — приходит LauchPad.

LaunchPad для демонстрации возможностей системы
LaunchPad для демонстрации возможностей системы

На этот раз разработчики подготовили официальное демо-приложение для своей CMS. С пользовательской функциональностью можно ознакомиться на сайте Strapi, а на GitHub появился репозиторий с технологическим стеком, в который входят:

10. Платные функции

10.1. Content History

При работе с контентом люди могут допускать ошибки. Порой доработки материалов не приносят ожидаемых результатов, и новые версии оказываются хуже предыдущих. Нововведение Content History из пятой версии Strapi предназначено для того, чтобы исправлять подобные ошибки без лишних усилий.

Теперь система запоминает все версии контента, которые вы сохраняете. К любой версии из истории изменений можно откатиться в несколько кликов. Старые версии хранятся в Content History с указанием даты сохранения и содержат информацию обо всех внесенных правках.

Стоит оговориться, что Content History – не бесплатное удовольствие; хранить старые версии можно либо по подписке в облаке Strapi, либо по расширенной корпоративной лицензии на собственных серверах. С ценниками можно ознакомиться на официальном сайте, но ссылку я не дам — в конце концов, это обзор, а не реклама. Зато дам ссылку на документацию, если вы захотите узнать подробности о Content History из первоисточника.

Content History
Content History

Перейти в Content History можно при редактировании документа в режиме Content Manager.  Для этого нужно выполнить следующие действия:

  1. нажать на «три точки» в правом верхнем углу экрана;

  2. в выпадающем списке выбрать «Content History».

Открывшийся раздел будет состоять из двух секций:

  • на панели справа будут отображены записи о старых версиях с указанием даты создания, времени изменения, автора изменений и статуса (Draft, Modified или Published);

  • в основной секции слева будет отображен контент, соответствующий записи, выбранной на правой панели.

При выборе версии в правой панели вы можете восстановить ее нажатием кнопки Restore. После этого актуальный черновик будет заменен на выбранную версию.

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

Content History помогает отслеживать, какие поля появлялись, исчезали или переименовывались в разных версиях. Неизвестные поля отображаются в разделе Unknown fields.

Поля в Content History
Поля в Content History

10.2. Расписание публикаций

Также обладателям подписки на Strapi 5будет доступна функция Release — с ее помощью можно запланировать для записей дату и время публикации. Может пригодиться для анонсов и рекламных постов, чтобы они автоматически публиковались в назначенное время.

Добавление нескольких элементов в расписание публикаций
Добавление нескольких элементов в расписание публикаций

11. Заключение

На мой взгляд, обновление вышло удачным — новые возможности Strapi действительно упрощают работу там, где раньше порой приходилось плясать с бубном. Функциональность по подписке может показаться тревожным звоночком, но пока она касается только тех возможностей, для реализации которых команде Strapi приходится хранить данные пользователей у себя в облаке.

Если вы по каким‑то причинам продолжаете пользоваться старой версией Strapi, сейчас самое время попробовать актуальную и сравнить их самостоятельно. Думаю, многие нововведения придутся вам по вкусу. А если вы не были знакомы с этой системой раньше, то загляните в мой предыдущий материал — там я подробно разбирал основные аспекты работы в ней:

CMS за 0 рублей: как мы начали использовать Strapi

А также читайте материалы моих коллег, которые тоже пишут о фронтенд‑разработке:

Комментарии (6)


  1. Kuch
    15.11.2024 21:09

    Интересно, content history работает только для измений через content manager? Или для всех изменений и через апи/контроллеры/entityService и тд. Если только через менеджер то совсем скучно, в принципе используя after update хук можно быстренько это все самому сделать


    1. kava13 Автор
      15.11.2024 21:09

      Да, Content History действительно работает только для изменений через Content Manager

      И да, при всем желании можно было бы самому сделать какой-то плагин, с помощью которого хранились бы все состояния сущности (даже при обновлении через entityService и т.д.), но помимо кастомизации хуков еще бы потребовалась кастомизация интерфейса Strapi

      В целом, теперь это решение как минимум предоставлено "из коробки" и можно быть уверенным в его поддержке от версии к версии


  1. lyssenkoalex
    15.11.2024 21:09

    Жаль нет локализации для enumerates.


    1. kava13 Автор
      15.11.2024 21:09

      Да, такого пока тоже не появилось

      Но, честно говоря, мне кажется что дублирование всех списков для разных языков – немножко избыточно и уводит нас от единообразия данных в ответе, и лучше чтобы локализация для значений enumerates была реализована уже на фронтенде

      Но, наверное, но для каких-то случаев это действительно могло бы стать преимуществом


  1. Bone
    15.11.2024 21:09

    А как перевести на русский язык админский интерфейс в strapi?


    1. kava13 Автор
      15.11.2024 21:09

      Встроенной такой функции нет, только через кастомизацию и добавление переводов каждому слову
      https://forum.strapi.io/t/unable-to-add-a-new-language-for-the-admin-ui/14251/6