Чому стандартних контролерів 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 по контролерам.







