Как @fastify/sensible улучшает опыт разработчика?
@fastify/sensible добавляет в reply методы-хелперы для HTTP-ошибок (reply.notFound(), reply.badRequest() и др.) и декораторы httpErrors, assert, to, делая код обработчиков короче и читаемее.
@fastify/sensible — набор полезных дополнений для Fastify
@fastify/sensible — официальный плагин, который добавляет к Fastify-приложению коллекцию небольших, но часто нужных улучшений. Он не меняет архитектуру приложения, а заполняет «пробелы», с которыми сталкиваются разработчики каждый день: стандартные HTTP-ошибки, безопасная обработка async-операций, удобные шорткаты для ответов.
Установка и регистрация
import Fastify from 'fastify';
import sensible from '@fastify/sensible';
const app = Fastify({ logger: true });
await app.register(sensible);
HTTP-ошибки через reply
После регистрации плагина на объект reply добавляются методы для всех стандартных HTTP-ошибок. Они автоматически устанавливают статус-код и возвращают объект ошибки в формате, совместимом с http-errors:
app.get('/users/:id', async (request, reply) => {
const user = await db.findById(request.params.id);
if (!user) {
// Вместо: return reply.code(404).send({ message: 'Not Found' })
return reply.notFound(`User ${request.params.id} not found`);
}
if (!request.user.canRead(user)) {
return reply.forbidden('Access denied');
}
return user;
});
Полный список методов ошибок
reply.badRequest('Invalid input'); // 400
reply.unauthorized('Login required'); // 401
reply.paymentRequired(); // 402
reply.forbidden('No access'); // 403
reply.notFound('Resource missing'); // 404
reply.methodNotAllowed(); // 405
reply.notAcceptable(); // 406
reply.conflict('Duplicate entry'); // 409
reply.gone(); // 410
reply.unprocessableEntity('Bad data'); // 422
reply.tooManyRequests('Slow down'); // 429
reply.internalServerError('Oops'); // 500
reply.notImplemented(); // 501
reply.badGateway(); // 502
reply.serviceUnavailable('Maintenance'); // 503
reply.gatewayTimeout(); // 504
httpErrors — создание ошибок вне обработчика
Декоратор app.httpErrors позволяет создавать объекты ошибок в middleware, сервисном слое или хуках, где reply недоступен:
// Сервисный слой
async function getUserOrThrow(id) {
const user = await db.findById(id);
if (!user) {
throw app.httpErrors.notFound(`User ${id} not found`);
}
return user;
}
// Обработчик просто вызывает сервис — ошибка всплывает автоматически
app.get('/users/:id', async (request) => {
return getUserOrThrow(request.params.id);
});
Утилита assert
app.assert(condition, statusCode, message) — компактный способ выбросить HTTP-ошибку при нарушении инварианта:
app.post('/transfer', async (request) => {
const { amount, toId } = request.body;
app.assert(amount > 0, 400, 'Amount must be positive');
app.assert(amount <= 10_000, 422, 'Amount exceeds limit');
const sender = await getUser(request.user.id);
app.assert(sender.balance >= amount, 409, 'Insufficient funds');
return transfer(sender, toId, amount);
});
Утилита to — безопасный await
app.to(promise) оборачивает Promise в стиле Go-like error handling, возвращая [error, data]:
app.get('/profile', async (request, reply) => {
const [err, user] = await app.to(fetchUser(request.user.id));
if (err) {
request.log.error(err, 'Failed to fetch user');
return reply.internalServerError('Could not load profile');
}
return user;
});
Подводные камни
- Инкапсуляция плагина: если
@fastify/sensibleзарегистрирован внутри дочернего контекста безfastify-plugin, методыreply.notFound()будут доступны только в этом контексте. Регистрируйте его на уровне корневого приложения или используйтеfp(). - Конфликт с setErrorHandler: если вы определили кастомный
setErrorHandler, убедитесь, что он корректно обрабатывает объекты ошибок изhttp-errors(у них есть поляstatusCodeиstatus). - app.to не логирует ошибки:
to()просто возвращает ошибку, не бросая её. Если забыть проверитьerr, ошибка будет молча проглочена. - assert бросает исключение: в отличие от библиотеки
assertиз Node.js,app.assertсоздаёт HTTP-ошибку, а неAssertionError. Не используйте её для unit-тестов — только для бизнес-инвариантов. - Сообщения ошибок попадают к клиенту: строки, переданные в
reply.forbidden('секретная причина'), будут видны в ответе. Не передавайте внутренние детали (имена таблиц, стек и т.д.).
Common mistakes
- Дает общий ответ про Node.js и не называет конкретные API Fastify.
- Не объясняет, где в lifecycle находится @fastify/sensible.
- Не разделяет validation, authorization, business logic и persistence.
- Игнорирует ошибки, лимиты входных данных, observability и тестирование.
What the interviewer is testing
- Может объяснить @fastify/sensible на примере кода.
- Называет ключевые API: httpErrors, assert.
- Использует точные API Fastify, а не вымышленные hooks/decorators/methods.
- Видит production-риски: безопасность, отказоустойчивость, логирование и тесты.