Разработка сайта на Strapi CMS
Strapi — Node.js headless CMS с автоматической генерацией REST и GraphQL API из content-type конфигурации. Content types описываются через JSON-схему (в src/api/*/content-types/*.json), изменения схемы через GUI сохраняются в коде. Подходит для проектов с частично техническими командами: разработчики описывают структуру, редакторы наполняют контент.
Архитектура
Browser / Mobile App / Next.js
↕ REST API / GraphQL
Strapi (Node.js)
↕
PostgreSQL / MySQL / SQLite
↕
Cloudinary / S3 (медиа)
Strapi работает как отдельный процесс. Нет монолитной интеграции с Next.js — только HTTP-запросы.
Установка
npx create-strapi-app@latest my-project --quickstart
# Quickstart: SQLite, без кастомизации
# Или с PostgreSQL
npx create-strapi-app@latest my-project \
--dbclient=postgres \
--dbhost=localhost \
--dbport=5432 \
--dbname=strapi_db \
--dbusername=strapi \
--dbpassword=pass
cd my-project
npm run develop # dev режим с hot reload
# Admin: http://localhost:1337/admin
Content Types
Content types создаются через Content-Type Builder в admin или вручную через JSON:
// src/api/article/content-types/article/schema.json
{
"kind": "collectionType",
"collectionName": "articles",
"info": {
"singularName": "article",
"pluralName": "articles",
"displayName": "Статья"
},
"attributes": {
"title": { "type": "string", "required": true },
"slug": { "type": "uid", "targetField": "title" },
"content": { "type": "richtext" },
"excerpt": { "type": "text", "maxLength": 500 },
"publishedAt": { "type": "datetime" },
"cover": { "type": "media", "multiple": false, "required": false, "allowedTypes": ["images"] },
"category": { "type": "relation", "relation": "manyToOne", "target": "api::category.category" },
"tags": { "type": "relation", "relation": "manyToMany", "target": "api::tag.tag" },
"author": { "type": "relation", "relation": "manyToOne", "target": "plugin::users-permissions.user" }
}
}
Типичный стек проекта
| Слой | Технология |
|---|---|
| CMS | Strapi 5.x |
| Frontend | Next.js 14 / Nuxt 3 |
| База данных | PostgreSQL |
| Медиа | Cloudinary / AWS S3 |
| Деплой CMS | Railway / Render / VPS |
| Деплой фронта | Vercel / Netlify |
| Кэш | Redis (для production) |
REST API из коробки
# Получить список статей (публичный, если настроены права)
GET http://localhost:1337/api/articles?populate=cover,category,author
# Фильтрация
GET /api/articles?filters[category][slug][$eq]=tech&sort=publishedAt:desc&pagination[pageSize]=10
# Конкретная запись
GET /api/articles/123?populate=deep
# Поиск
GET /api/articles?filters[title][$containsi]=javascript
Подключение к Next.js
// lib/strapi.ts
const STRAPI_URL = process.env.STRAPI_URL || 'http://localhost:1337'
const API_TOKEN = process.env.STRAPI_API_TOKEN
export async function fetchStrapi<T>(
endpoint: string,
options?: RequestInit
): Promise<T> {
const response = await fetch(`${STRAPI_URL}/api${endpoint}`, {
headers: {
Authorization: `Bearer ${API_TOKEN}`,
'Content-Type': 'application/json',
},
next: { tags: [endpoint.split('/')[1]] }, // ISR тег
...options,
})
if (!response.ok) {
throw new Error(`Strapi API error: ${response.status}`)
}
const data = await response.json()
return data
}
// Использование
const { data: articles } = await fetchStrapi<{ data: Article[] }>(
'/articles?populate=cover,category&sort=publishedAt:desc&pagination[pageSize]=10'
)
Вебхуки для ISR
// app/api/revalidate/strapi/route.ts
import { revalidateTag } from 'next/cache'
import { NextRequest, NextResponse } from 'next/server'
export async function POST(req: NextRequest) {
const body = await req.json()
// Strapi шлёт: { event, uid, model, entry }
const { model } = body
// Инвалидировать кэш по модели
revalidateTag(model)
return NextResponse.json({ revalidated: true })
}
В Strapi admin: Settings → Webhooks → Add new webhook → URL вашего Next.js эндпоинта.
Особенности разработки
Формат ответа Strapi: все данные оборачиваются в { data: { id, attributes: {...} } }. В Strapi 5 это изменилось — данные возвращаются плоско.
Populate: по умолчанию связи не заполняются. Нужно явно указывать ?populate=* или ?populate[category][populate][0]=icon.
Черновики: встроена Draft/Publish система. ?publicationState=live — только опубликованные, ?publicationState=preview + API token — черновики.
Сроки
Базовый сайт с 4–6 content types, настройкой прав и интеграцией с Next.js — 2–3 недели. Сложный проект с кастомными контроллерами, плагинами, мультиязычностью — 4–6 недель.







