Middleware — антипаттерн (альтернативная позиция)
Альтернативная позиция (контр-консенсус, 2026): middleware-паттерн (Connect/Express) — антипаттерн в Node.js. Reference pollution, race conditions, неявный shared state, толстые контроллеры.
В чём суть проблемы
- Контракт
(req, res, next)— middleware миксинит свои поля в request/response. Полевая загрязнённость в обе стороны. - Reference pollution: ссылки на req/res утекают в разные части приложения, у них нет единого владельца.
- Shared state на уровне модуля (например,
let userIdдля всего модуля) → конкурентные пользователи перетирают друг друга. - Утечка абстракций: непонятно, какая middleware за что отвечает — куки или auth?
- Порядок критичен: переставил местами — сломалось. Люди запускают legacy-проект методом тыка middleware.
- Race conditions на одном response: одна middleware пишет в response, другая ещё хочет писать.
Признаки антипаттерна
// shared state на модуле — катастрофа при конкуренции
let userId;
app.use((req, res, next) => { userId = req.session.userId; next; });
app.use((req, res, next) => { checkAccess(userId); next; }); // не тот юзер!
// миксинг полей в req
app.use((req, res, next) => { req.customAuth = doAuth(req); next; });
// в контроллере: что такое req.customAuth? кто положил? когда?
// EventEmitter держит ссылку на response
emitter.on('timeout', () => { if (!res.ended) res.send('timeout'); });
// race: res уже завершён другим путём
Чем заменить
- NestJS — изолирует от middleware-проблем сервисами/роутингом.
- Слоистая/гексагональная архитектура — middleware заменяется явными слоями: pipeline-валидации, use cases, репозитории.
- Контексты вместо shared state — каждый запрос имеет свой immutable context.
- DDD-подход — бизнес-логика не знает про req/res.
Когда middleware OK
- Лог-функции, не меняющие состояние (доступ только на чтение).
- Адаптеры/декораторы из библиотек, не миксящие в req/res.
Антипаттерн (полный пример)
// толстый контроллер на middleware-цепочке
app.post('/order',
authMiddleware, // кладёт req.user
validateMiddleware,// кладёт req.validated
rateLimit, // меняет shared rate-limit state
async (req, res) => {
if (!req.user) return res.status(401).end(); // дубль auth
const order = await db.query(`INSERT…`, req.validated);
await mailer.send(req.user.email, 'Order'); // ещё ответственность
res.json(order);
}
);
🎓 Источники
- 🎓 [Middleware антипаттерн для Node.js в 2026] · 2026-03-13 · YouTube
- Альтернативная позиция: «На 26-й год middleware в Node.js — сплошной антипаттерн».
- «Reference pollution в две стороны, race conditions, shared state — это всё опасно. Как только middleware больше одной — конфликты».
- «Разработчики переставляют middleware местами, чтобы запустился legacy».
- 🎓 [Middleware антипатерн (українською)] · 2026-03-12 · YouTube
- Контексты как альтернатива middleware. Nest скрывает middleware за сервисами.