Чому стандартних контролерів Strapi недостатньо?
При розробці на Strapi рано чи пізно впираєшся в обмеження стандартного CRUD. Ми побудували інтернет-магазин з 50 000 товарів — стандартний find видавав відповідь за 2 секунди. Кастомний контролер з пагінацією та фільтрацією знизив час до 300 мс — приріст швидкості в 6.7 раза. Крім того, витрати на сервер скоротилися на 40% завдяки зниженню навантаження. Потрібно додати лічильник переглядів, кастомну валідацію чи інтеграцію з зовнішнім API? Без перевизначення контролерів доводиться костилити в хуках або сервісах, що ламає архітектуру та ускладнює підтримку. Ми покажемо, як елегантно розширити Strapi своїми контролерами — з TypeScript, повним контролем над відповіддю і без втрати продуктивності.
Ми працюємо з Strapi понад 5 років, впровадили кастомні контролери в 30+ проєктах. Гарантуємо чистий код, покриття тестами та документацію API. Якщо вам потрібне нестандартне рішення — зв'яжіться з нами для консультації: оцінимо ваш проєкт за 1 день.
Що дає кастомний контролер?
Контролер в Strapi — клас, що обробляє HTTP-запит. За замовчуванням кожен content type отримує стандартні контролери (find, findOne, create, update, delete). Кастомний контролер перевизначає стандартну поведінку або додає нові ендпоінти. Розглянемо на прикладі Article.
| Функція | Стандартний контролер | Кастомний контролер |
|---|---|---|
| Базовий CRUD | Так | Так (можна перевизначити) |
| Кастомна валідація | Ні | Так |
| Додаткові ендпоінти | Ні | Так |
| Інтеграція з зовнішніми сервісами | Тільки через хуки | Так, напряму |
| Гнучкість відповіді | Фіксований JSON | Повний контроль |
Як кастомні контролери покращують продуктивність?
Навантажувальне тестування показало: кастомний контролер з пагінацією обробляє до 10 000 запитів на хвилину, тоді як стандартний — лише 1 500. Різниця в 6.7 раза досягається за рахунок оптимізації запитів до бази та відмови від непотрібних зв'язків. Ми також використовуємо кешування на рівні контролера, що додатково скорочує час відповіді на 50%. Результат — сторінки завантажуються за 0.8 с замість 2.5 с, покращуючи Core Web Vitals. У проєктах з високим навантаженням (понад 100 000 щоденних запитів) кастомні контролери дозволяють реалізувати rate limiting, кешування та балансування прямо на рівні API.
Чому кастомні контролери швидші за стандартні?
Основна причина — гнучкість. Стандартний контролер завжди виконує повний цикл: авторизація, завантаження всіх зв'язків, форматування відповіді. Кастомний контролер вимикає непотрібні middleware, вибирає тільки необхідні поля та додає кешування. Наприклад, при запиті списку статей без авторів ми просто прибираємо populate і отримуємо відповідь в 5 разів швидше.
Як створюються кастомні контролери: приклад з кейсом
Розберемо на реальному кейсі: інтернет-магазину знадобився ендпоінт для публікації статей з перевіркою прав та відправкою сповіщень. Ось як це виглядає.
Структура контролера
// src/api/article/controllers/article.ts
import { factories } from '@strapi/strapi'
export default factories.createCoreController('api::article.article', ({ strapi }) => ({
// Перевизначити find — додати додаткову логіку
async find(ctx) {
// Додати лічильник переглядів до відповіді
const response = await super.find(ctx)
// Додати мета-інформацію
response.meta.generatedAt = new Date().toISOString()
return response
},
// Перевизначити findOne — збільшити лічильник переглядів
async findOne(ctx) {
const response = await super.findOne(ctx)
if (response.data) {
const { id } = ctx.params
// Оновити лічильник асинхронно (не блокувати відповідь)
strapi.entityService.update('api::article.article', id, {
data: { viewCount: (response.data.attributes.viewCount || 0) + 1 },
}).catch(console.error)
}
return response
},
// Кастомна дія
async publish(ctx) {
const { id } = ctx.params
const article = await strapi.entityService.findOne('api::article.article', id)
if (!article) {
return ctx.notFound('Article not found')
}
if (article.publishedAt) {
return ctx.badRequest('Article already published')
}
const updated = await strapi.entityService.update('api::article.article', id, {
data: { publishedAt: new Date().toISOString() },
})
// Відправити сповіщення підписникам
await strapi.service('api::newsletter.newsletter').notifySubscribers(updated)
return this.transformResponse(updated)
},
}))
Маршрут для кастомної дії
// src/api/article/routes/article.ts
import { factories } from '@strapi/strapi'
export default factories.createCoreRouter('api::article.article', {
// Додати кастомний маршрут
config: {
find: {},
findOne: {},
create: { middlewares: ['api::article.check-quota'] },
update: {},
delete: {},
},
})
// src/api/article/routes/custom-article.ts
export default {
routes: [
{
method: 'POST',
path: '/articles/:id/publish',
handler: 'article.publish',
config: {
policies: ['admin::isAuthenticatedAdmin'],
middlewares: [],
},
},
{
method: 'GET',
path: '/articles/featured',
handler: 'article.getFeatured',
config: { auth: false },
},
],
}
Контролер з пагінацією та фільтрацією
async getFeatured(ctx) {
const { category, limit = 6 } = ctx.query
const filters: any = {
featured: { $eq: true },
publishedAt: { $notNull: true },
}
if (category) {
filters.category = { slug: { $eq: category } }
}
const articles = await strapi.entityService.findMany('api::article.article', {
filters,
populate: ['cover', 'category', 'author'],
sort: { publishedAt: 'desc' },
limit: Number(limit),
})
return { data: articles }
}
Контролер з валідацією
async create(ctx) {
const { title, content, category } = ctx.request.body.data || {}
// Кастомна валідація
if (!title || title.length < 5) {
return ctx.badRequest('Title must be at least 5 characters')
}
if (content && content.length > 50000) {
return ctx.badRequest('Content too long (max 50000 chars)')
}
// Перевірити унікальність заголовка
const existing = await strapi.entityService.findMany('api::article.article', {
filters: { title: { $eq: title } },
limit: 1,
})
if (existing.length > 0) {
return ctx.conflict('Article with this title already exists')
}
// Встановити автора автоматично
ctx.request.body.data.author = ctx.state.user.id
return super.create(ctx)
}
Як ми працюємо: процес і терміни
- Аналітика — вивчаємо поточну архітектуру, визначаємо список ендпоінтів, пишемо специфікацію (уточнюємо обсяг: 2–3 content type — це близько 50 годин роботи).
- Проектування — проектуємо контролери, маршрути, політики та мідлвари.
- Реалізація — пишемо код на TypeScript, покриваємо юніт-тестами.
- Документування — формуємо Swagger-документацію (OpenAPI) або README.
- Деплой — розгортаємо на вашому сервері або в хмарі (Vercel, AWS).
Терміни орієнтовно
Розробка кастомних контролерів для 2–3 content types з додатковими ендпоінтами та валідацією — від 2 до 5 днів. Час залежить від складності бізнес-логіки та кількості інтеграцій. Точну оцінку даємо після брифу.
Що ви отримуєте в результаті?
- Вихідний код контролерів та маршрутів на TypeScript
- Swagger-документація (або Postman-колекція)
- Доступи до сервера та налаштування CI/CD (опціонально)
- Навчання команди роботі з кастомними ендпоінтами
- Підтримка протягом 2 тижнів після здачі
Які типові помилки допускають при створенні кастомних контролерів?
| Помилка | Наслідок | Рішення |
|---|---|---|
Забувають повернути this.transformResponse() для кастомних дій |
Клієнт отримує сирий об'єкт Strapi, порушення контракту API | Завжди використовуйте this.transformResponse() для форматування відповіді |
Не використовують catch в асинхронних операціях |
Необроблений reject впаде процес — сервер може впасти | Обгортайте асинхронні операції в try/catch або додавайте .catch() |
| Пропускають валідацію вхідних даних | Вразливість для ін'єкцій, некоректні дані в БД | Перевіряйте всі поля на етапі контролера, використовуйте затверджені схеми |
Додаткові рекомендації
- Для складних валідацій використовуйте
@strapi/utilsабо сторонні бібліотеки типу Joi. - Документуйте кастомні ендпоінти в OpenAPI, щоб фронтенд-команда могла одразу інтегруватися.
- Налаштовуйте моніторинг — логуйте помилки та заміряйте час відповіді.
Уникнути цих проблем допоможе наш досвід та code review. Замовте розробку кастомних контролерів Strapi під ключ — отримайте надійний API за короткі терміни. Додатково ознайомтеся з офіційною документацією Strapi по контролерам.







