Кастомные middleware Strapi
Ваш Strapi-проект тормозит при 1000 запросах в минуту? Логи разбросаны, а клиенты жалуются на 429 ошибки? Типовая ситуация: навигация начинает подтормаживать, а время ответа проседает до 800 мс. Чтобы это исправить, мы внедряем кастомные middleware — функции, перехватывающие запросы до и после обработки контроллером. Наш опыт: реализовали такие middleware для 50+ проектов — от интернет-магазинов до сложных CMS. Кастомные middleware Strapi позволяют гибко управлять запросами: ограничивать частоту, логировать, проверять подписки, трансформировать ответы.
Middleware (промежуточное ПО) — это концепция, знакомая разработчикам Express или Koa: они работают в цепочке, передавая управление через вызов await next(). Strapi поддерживает два типа: глобальные (применяются ко всем маршрутам) и маршрутные (только к определённым). Вот их сравнение:
| Тип | Область применения | Регистрация |
|---|---|---|
| Global | Все запросы | config/middlewares.js |
| Route | Конкретный маршрут | Конфигурация маршрута |
Как rate limiting влияет на производительность Strapi?
In-memory rate limiting — один из самых востребованных middleware. Он хранит временные метки запросов по IP в Map. Как только количество запросов за окно (например, 100 за 60 секунд) превышает лимит, возвращается 429. In-memory быстрее файлового в 10 раз — задержка минимальна. Для распределённых систем используем Redis; мы помогаем и с этим. Вот типовой код:
// src/middlewares/rate-limit.ts
const requests = new Map<string, number[]>()
export default (config: any) => {
const { maxRequests = 100, windowMs = 60_000 } = config
return async (ctx: any, next: any) => {
const ip = ctx.request.ip
const now = Date.now()
const windowStart = now - windowMs
const timestamps = (requests.get(ip) || []).filter(t => t > windowStart)
if (timestamps.length >= maxRequests) {
ctx.status = 429
ctx.body = { error: 'Too Many Requests' }
ctx.set('Retry-After', String(Math.ceil(windowMs / 1000)))
return
}
timestamps.push(now)
requests.set(ip, timestamps)
await next()
}
}Конфигурация в config/middlewares.js подключается как 'global::rate-limit' с параметрами maxRequests и windowMs.
In-memory rate limiting с Map даёт задержку менее 1 мс, Redis — 1-3 мс, но масштабируется горизонтально. Для проектов с нагрузкой до 1000 запросов/сек достаточно in-memory, от 1000 до 5000 — используем Redis. На одном проекте (интернет-магазин) после внедрения rate limiting и кеширующего middleware TTFB упал с 800 мс до 200 мс, а 429 ошибки исчезли полностью. Это позволило сэкономить около $500 в месяц на серверных ресурсах (расчёт экономии по проекту).
Зачем логировать запросы через middleware?
Стандартный логгер Strapi не умеет отслеживать медленные запросы. Кастомный middleware с таймингом решает это: логирует все запросы и помечает медленные (более 1 секунды). Это помогает находить узкие места до того, как они станут критическими. Пример:
// src/middlewares/request-logger.ts
export default (config: any, { strapi }: any) => {
return async (ctx: any, next: any) => {
const start = Date.now()
await next()
const duration = Date.now() - start
const { method, url, status } = ctx
if (duration > 1000) {
strapi.log.warn(`Slow request: ${method} ${url} — ${duration}ms (${status})`)
}
strapi.log.debug(`${method} ${url} — ${duration}ms [${status}]`)
}
}Подключается глобально через config/middlewares.js. Гарантируем, что после настройки вы не пропустите ни одного проблемного запроса.
Как мы реализуем проверку подписки через middleware?
Для премиум-контента используем route middleware, проверяющий активную подписку у пользователя. Такой middleware применяется только к одному маршруту, не нагружая остальные:
// src/middlewares/check-subscription.ts
export default (config: any, { strapi }: any) => {
return async (ctx: any, next: any) => {
const userId = ctx.state.user?.id
if (!userId) {
ctx.unauthorized('Authentication required')
return
}
const user = await strapi.entityService.findOne(
'plugin::users-permissions.user',
userId,
{ populate: ['subscription'] }
)
if (!user?.subscription?.active) {
ctx.forbidden('Active subscription required')
return
}
await next()
}
}В конфигурации маршрута добавляется поле middlewares: ['api::check-subscription'].
Почему middleware важны для Core Web Vitals?
Каждый middleware может улучшить Core Web Vitals. Например, кеширующий middleware сокращает TTFB, а rate limiting предотвращает перегрузку сервера и падение INP. Если ваш API возвращает данные за 800 мс, это напрямую влияет на LCP. Middleware могут кешировать ответы или ограничивать частоту, снижая задержку.
Трансформация ответов и мультиязычность
Часто нужно добавить вычисляемое поле — например, размер скидки. Middleware перехватывает ответ и добавляет discountPercent. А для мультиязычных проектов — middleware, автоматически определяющий локаль из заголовка Accept-Language:
// src/middlewares/add-computed-fields.ts
export default () => {
return async (ctx: any, next: any) => {
await next()
if (ctx.url.startsWith('/api/products') && ctx.body?.data) {
const transform = (item: any) => ({
...item,
attributes: {
...item.attributes,
discountPercent: item.attributes.originalPrice
? Math.round((1 - item.attributes.price / item.attributes.originalPrice) * 100)
: 0,
},
})
if (Array.isArray(ctx.body.data)) {
ctx.body.data = ctx.body.data.map(transform)
} else {
ctx.body.data = transform(ctx.body.data)
}
}
}
} // src/middlewares/locale-redirect.ts
const localeMap: Record<string, string> = {
'ru-RU': 'ru',
'en-US': 'en',
'uk-UA': 'uk',
}
export default () => {
return async (ctx: any, next: any) => {
if (!ctx.query.locale) {
const acceptLang = ctx.get('Accept-Language')?.split(',')[0] || 'ru'
const locale = localeMap[acceptLang] || acceptLang.split('-')[0] || 'ru'
ctx.query.locale = locale
}
await next()
}
} Какие middleware нужны для продакшена?
Обязательный набор включает rate limiting, логирование, аутентификацию/авторизацию и трансформацию ответов. Дополнительно можно добавить кеширование, редиректы, CORS. Мы помогаем подобрать оптимальный набор под вашу нагрузку. Стоимость разработки комплекта из 3-4 middleware варьируется от $100–500. в зависимости от сложности. Точную стоимость рассчитываем индивидуально после анализа вашего проекта.
Пошаговая настройка rate limiting middleware
- Создайте файл
src/middlewares/rate-limit.tsс кодом из примера. - В
config/middlewares.jsдобавьте'global::rate-limit'в массивmiddlewares. - Настройте параметры
maxRequestsиwindowMsпод вашу нагрузку. - Протестируйте локально командой
npx strapi develop. - Для продакшена используйте Redis через внешнее хранилище.
Подробнее о конфигурации middleware читайте в официальной документации Strapi.
Процесс работы и что входит
Мы работаем так: анализируем требования → проектируем архитектуру middleware → пишем код с тестами → разворачиваем на staging → деплоим в продакшен. В результат входит:
- Исходный код middleware с комментариями
- Документация по установке и настройке
- Доступ к репозиторию (Git)
- Обучение вашей команды (1 час онлайн)
- Поддержка 2 недели после запуска
Сроки ориентировочно
Разработка набора из 3-4 middleware (rate limiting, логирование, проверка подписки, трансформация) занимает от 1 до 2 дней. Стоимость рассчитывается индивидуально.
Свяжитесь с нами для оценки вашего проекта — мы подберём оптимальный набор middleware под вашу нагрузку. Получите консультацию по кастомным middleware Strapi и закажите разработку под ключ с гарантией 2 недели поддержки.







