Хук onRequest выполняется самым первым в жизненном цикле обработки HTTP-запроса. Его основная задача — перехватить запрос до того, как он попадёт в маршрутизацию, обработчики или систему валидации.
В этот момент доступны только базовые данные запроса: заголовки,
метод, URL и низкоуровневые параметры соединения. Это делает
onRequest идеальным местом для операций, которые должны
происходить максимально рано.
onRequest(request, reply)
Где:
request — объект входящего запросаreply — объект управления ответом1. Логирование входящих запросов
app.onRequest((request, reply) => {
console.log(`[${request.method}] ${request.url}`);
});
На этом уровне фиксируются все обращения к серверу, включая потенциально невалидные.
2. Предварительная фильтрация IP
const blockedIPs = ['192.168.0.10'];
app.onRequest((request, reply) => {
const ip = request.ip;
if (blockedIPs.includes(ip)) {
reply.code(403).send({ error: 'Forbidden' });
}
});
Важно: на этом этапе можно полностью остановить обработку запроса.
3. Установка глобальных контекстных данных
app.onRequest((request, reply) => {
request.startTime = Date.now();
});
Такие данные часто используются позже для метрик и профилирования.
Хук onRequest не должен содержать тяжёлую бизнес-логику.
Его задача — подготовка и первичная проверка запроса. Любые сложные
вычисления или обращения к БД здесь ухудшают общую производительность
системы.
Хук preHandler выполняется после маршрутизации запроса, но до вызова основного обработчика (handler). На этом этапе уже известно, какой endpoint будет вызван, и доступны параметры маршрута, query и body (если они были распарсены).
preHandler(request, reply)
1. Проверка авторизации
app.preHandler((request, reply) => {
const token = request.headers.authorization;
if (!token) {
return reply.code(401).send({ error: 'Unauthorized' });
}
request.user = decodeToken(token);
});
Здесь выполняется ключевая проверка доступа к ресурсу.
2. Валидация бизнес-правил
app.preHandler(async (request, reply) => {
const { id } = request.params;
const exists = await db.users.exists(id);
if (!exists) {
return reply.code(404).send({ error: 'User not found' });
}
});
В отличие от схемной валидации, здесь можно проверять данные через внешние системы.
3. Модификация запроса перед обработчиком
app.preHandler((request) => {
request.meta = {
locale: request.headers['accept-language'] || 'en'
};
});
Это позволяет централизованно добавлять данные, которые будут использоваться в handler.
onRequestpreHandleronRequest
preHandler
Хуки формируют цепочку, в которой каждый следующий этап зависит от предыдущего. Ошибка или завершение ответа в любом из них останавливает дальнейшее выполнение.
Request → onRequest → Router → preHandler → Handler → Response
Если в onRequest возвращён ответ,
preHandler и handler не выполняются.
Оба хука поддерживают async функции, что позволяет
выполнять операции с сетью или базами данных.
app.preHandler(async (request, reply) => {
const session = await redis.get(request.headers.token);
if (!session) {
reply.code(401).send();
}
});
Важно учитывать, что задержки в этих хуках напрямую увеличивают latency запроса.
Ошибка, выброшенная в onRequest, блокирует дальнейшую
обработку запроса сразу.
app.onRequest(() => {
throw new Error('Critical failure');
});
Аналогично, ошибка в preHandler предотвращает вызов
handler.
onRequest
preHandler
Размещение сложной логики в onRequest приводит к
ухудшению производительности всей системы, так как он выполняется для
каждого запроса вне зависимости от маршрута.
Если проверка авторизации дублируется в onRequest и
preHandler, это создаёт избыточность и усложняет
сопровождение кода.
Синхронные тяжёлые операции в хуках увеличивают время ответа и могут блокировать event loop.
И onRequest, и preHandler часто
используются для обогащения объекта request, чтобы
передавать данные дальше по цепочке:
app.onRequest((request) => {
request.traceId = generateTraceId();
});
app.preHandler((request) => {
request.userContext = getUserContext(request);
});
Так формируется единый контекст запроса, который затем используется в handler и логировании.
Если в любом из хуков вызывается:
reply.send()
или устанавливается код ответа, последующие этапы обработки не выполняются. Это позволяет реализовывать быстрые выходы без загрузки маршрутов и бизнес-логики.
Хуки могут быть объявлены глобально или на уровне конкретного маршрута:
app.get('/profile', {
preHandler: (request, reply) => {
if (!request.user) {
reply.code(401).send();
}
}
}, handler);
Такой подход позволяет строить модульную систему обработки запросов с переиспользуемыми слоями логики.