Користувацькі контролери в Strapi
Контролер в Strapi — це клас, який обробляє HTTP-запити. За замовчуванням кожен тип контенту отримує стандартні контролери (find, findOne, create, update, delete). Користувацький контролер перевизначає стандартну поведінку або додає нові ендпоінти.
Структура контролера
// 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 типів контенту з додатковими ендпоінтами та валідацією займає 2–3 дні.







