Интеграция CMS Strapi для управления контентом
Strapi — headless CMS с открытым исходным кодом на Node.js. Предоставляет административную панель для управления контентом и автоматически генерирует REST и GraphQL API по созданным типам контента. Разворачивается на собственном сервере — данные остаются под контролем.
Архитектура
Strapi состоит из двух частей: Admin Panel — React-приложение для управления контентом, схемами и настройками; Backend — Node.js/Koa сервер с автогенерируемыми маршрутами API.
Схема работы в headless-режиме:
Strapi Admin → создаёт контент
Strapi API → отдаёт контент клиентам
Frontend → Next.js / Nuxt / мобильное приложение
Установка
npx create-strapi-app@latest my-cms --quickstart
# или с указанием базы данных
npx create-strapi-app@latest my-cms \
--dbclient=postgres \
--dbhost=127.0.0.1 \
--dbport=5432 \
--dbname=strapi \
--dbusername=strapi \
--dbpassword=secret
Поддерживаемые базы данных: SQLite (для разработки), PostgreSQL, MySQL/MariaDB.
Content Types
Типы контента создаются через Content-Type Builder в admin-панели (drag-and-drop) или вручную в коде:
src/api/article/content-types/article/schema.json
{
"kind": "collectionType",
"collectionName": "articles",
"info": {
"singularName": "article",
"pluralName": "articles",
"displayName": "Article"
},
"attributes": {
"title": { "type": "string", "required": true },
"slug": { "type": "uid", "targetField": "title" },
"content": { "type": "richtext" },
"cover": { "type": "media", "allowedTypes": ["images"] },
"author": { "type": "relation", "relation": "manyToOne", "target": "plugin::users-permissions.user" },
"publishedAt": { "type": "datetime" }
}
}
Изменения схемы автоматически создают миграции при следующем запуске.
API-запросы
# Получить все статьи
GET /api/articles
# С populate для связанных данных
GET /api/articles?populate=cover,author
# Фильтрация
GET /api/articles?filters[title][$containsi]=strapi&filters[publishedAt][$notNull]=true
# Сортировка и пагинация
GET /api/articles?sort=publishedAt:desc&pagination[page]=1&pagination[pageSize]=10
Ответ:
{
"data": [
{
"id": 1,
"attributes": {
"title": "Getting started with Strapi",
"slug": "getting-started",
"content": "<p>...</p>",
"publishedAt": "2025-03-01T10:00:00.000Z",
"cover": { "data": { "attributes": { "url": "/uploads/cover.jpg" } } }
}
}
],
"meta": { "pagination": { "page": 1, "pageSize": 10, "total": 42 } }
}
Кастомные маршруты и контроллеры
// src/api/article/routes/custom-article.js
module.exports = {
routes: [
{
method: 'GET',
path: '/articles/featured',
handler: 'article.featured',
config: { policies: [], middlewares: [] },
},
],
};
// src/api/article/controllers/article.js
module.exports = {
async featured(ctx) {
const articles = await strapi.entityService.findMany('api::article.article', {
filters: { featured: true, publishedAt: { $notNull: true } },
sort: { publishedAt: 'desc' },
limit: 6,
populate: ['cover'],
});
return { data: articles };
},
};
Middleware и Policies
// src/api/article/policies/is-author.js
module.exports = async (policyContext, config, { strapi }) => {
const { id } = policyContext.params;
const userId = policyContext.state.user?.id;
const article = await strapi.entityService.findOne('api::article.article', id);
return article.author?.id === userId;
};
Lifecycle Hooks
// src/api/article/content-types/article/lifecycles.js
module.exports = {
async beforeCreate(event) {
const { data } = event.params;
if (!data.slug) {
data.slug = slugify(data.title);
}
},
async afterCreate(event) {
const { result } = event;
// уведомить Telegram, сбросить кеш, индексировать в Algolia
await notifyChannel('new-article', result.id);
},
};
Интернационализация (i18n)
Strapi имеет встроенный плагин i18n:
# Включить в admin: Settings → Internationalization → Add locale
В схеме типа контента включить pluginOptions: { i18n: { localized: true } } для нужных полей. API: GET /api/articles?locale=ru.
Деплой на продакшен
# docker-compose.yml (упрощённо)
services:
strapi:
image: node:20-alpine
command: yarn start
environment:
NODE_ENV: production
DATABASE_URL: postgres://user:pass@db:5432/strapi
JWT_SECRET: ${JWT_SECRET}
ADMIN_JWT_SECRET: ${ADMIN_JWT_SECRET}
APP_KEYS: ${APP_KEYS}
volumes:
- ./public/uploads:/app/public/uploads
db:
image: postgres:16
volumes:
- pgdata:/var/lib/postgresql/data
Для production рекомендуется выносить медиафайлы в S3 через @strapi/provider-upload-aws-s3.
Сроки
Установка, настройка типов контента, REST API, права доступа, деплой — 3–5 рабочих дней. С кастомными плагинами, i18n, вебхуками для фронта, мониторингом — 1–2 недели.







