Что такое middleware в Fastify и чем он отличается от Express?
Middleware в Express
В Express middleware — это функция вида (req, res, next), которая выполняется последовательно для каждого запроса, проходящего через app.use(). Middleware может модифицировать req/res, завершать цикл ответа или передавать управление дальше вызовом next(). Ошибки передаются через next(err), и они попадают в специальный error-handling middleware с четырьмя аргументами (err, req, res, next).
Важная особенность Express — middleware выполняется линейно, одно за другим, без явной привязки к конкретной фазе обработки запроса (парсинг, валидация, авторизация и т.д. — всё это просто отдельные middleware в общей цепочке).
Как устроено в Fastify
Fastify спроектирован иначе: вместо единой цепочки middleware у него есть система хуков (hooks), каждый из которых привязан к конкретному этапу жизненного цикла запроса:
onRequest— самый ранний этап, до парсинга телаpreParsing— перед парсингом тела запросаpreValidation— перед валидацией по JSON-схемеpreHandler— перед основным обработчиком роутаpreSerialization/onSend— перед отправкой ответаonResponse,onError— после ответа / при ошибке
Хуки регистрируются через fastify.addHook('onRequest', async (request, reply) => {...}) и могут быть асинхронными — Fastify нативно построен вокруг async/await, поэтому вместо next(err) для сигнализации об ошибке достаточно просто выбросить исключение (throw) или вернуть отклонённый промис.
Инкапсуляция через плагины
Ещё одно ключевое отличие — инкапсуляция контекста. В Express всё, что зарегистрировано через app.use(), глобально влияет на всё приложение. В Fastify хуки и декораторы, зарегистрированные внутри плагина (fastify.register()), по умолчанию видны только внутри этого плагина и его дочерних роутов, а не во всём приложении. Это позволяет строить модульные, изолированные части API (например, разная авторизация для /public и /admin), не боясь случайно "утечь" логику в соседние модули.
Совместимость с express-style middleware
Поскольку многие разработчики привыкли к middleware-экосистеме Express (cors, helmet, morgan и т.д.), Fastify предоставляет официальный плагин @fastify/middie, который позволяет подключать обычные (req, res, next) middleware внутри Fastify-приложения:
await fastify.register(require('@fastify/middie'))
fastify.use(require('cors')())
Это в первую очередь механизм для миграции и переиспользования существующих Express-совместимых пакетов, а не основной способ работы с Fastify.
Почему это важно на собеседовании
- Fastify быстрее Express во многом благодаря тому, что не тянет за собой универсальный слой middleware, а даёт точечные хуки только там, где они нужны
- Встроенная валидация через JSON-схемы интегрирована в жизненный цикл (
preValidation) — в Express это делается вручную через отдельные middleware - Асинхронная модель ошибок (throw/reject) вместо
next(err)упрощает работу сasync/await
Что хочет услышать интервьюер
Кандидат понимает базовую модель middleware в Express: (req, res, next), линейная цепочка, next(err) для ошибок
Кандидат знает, что в Fastify вместо middleware используются хуки, привязанные к конкретным фазам жизненного цикла запроса
Упоминание инкапсуляции — хуки/декораторы плагина не 'протекают' в глобальную область видимости
Понимание, что Fastify поддерживает express-style middleware через отдельный плагин (@fastify/middie), но это не основной подход
Связь архитектурных отличий с производительностью и встроенной JSON-schema валидацией
Пример: Middleware в Express
const express = require('express')
const app = express()
// обычное middleware — выполняется для каждого запроса
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`)
next() // передаём управление дальше по цепочке
})
// middleware с проверкой авторизации
app.use('/admin', (req, res, next) => {
if (!req.headers.authorization) {
return next(new Error('Unauthorized')) // ошибка уходит в error-handler
}
next()
})
app.get('/admin/dashboard', (req, res) => {
res.send('dashboard')
})
// error-handling middleware — четыре аргумента
app.use((err, req, res, next) => {
res.status(401).send(err.message)
})
Пример: Эквивалент через хуки в Fastify
const fastify = require('fastify')()
// хук onRequest — выполняется на самом раннем этапе
fastify.addHook('onRequest', async (request, reply) => {
console.log(`${request.method} ${request.url}`)
})
// плагин с инкапсулированной проверкой авторизации
// хук ниже действует только внутри этого плагина, а не глобально
fastify.register(async function adminPlugin(instance) {
instance.addHook('preHandler', async (request, reply) => {
if (!request.headers.authorization) {
// просто бросаем ошибку — не нужен next(err)
throw new Error('Unauthorized')
}
})
instance.get('/admin/dashboard', async (request, reply) => {
return 'dashboard'
})
}, { prefix: '/admin' })
// глобальный обработчик ошибок
fastify.setErrorHandler((err, request, reply) => {
reply.status(401).send({ error: err.message })
})
Типичные ошибки
Утверждение, что в Fastify middleware работает точно так же, как в Express, просто с другим синтаксисом
Незнание про инкапсуляцию плагинов — предположение, что всё зарегистрированное глобально видно везде, как в Express
Путаница между хуками (onRequest, preHandler и т.д.) и плагинами — кандидат считает их одним и тем же механизмом
Забывают, что для ошибок в Fastify достаточно throw/reject, и пытаются описывать next(err)-подобный паттерн
Не знают о существовании @fastify/middie и утверждают, что Express-middleware в Fastify использовать вообще нельзя


