fastify-http-exceptions is a small, opinionated Fastify plugin and core library for typed HTTP exceptions.
It lets you throw HTTP exceptions with structured error payloads in your route handlers and have them automatically converted into proper Fastify responses.
The core utilities are framework‑agnostic; the Fastify plugin was created out of real‑world need to simplify error handling in larger APIs.
- Typed HTTP exception hierarchy:
BadRequestException,UnauthorizedException,ForbiddenException,NotFoundException, and many more. - Consistent JSON error bodies: 4xx/5xx errors are serialized as
{ error: string }. - Redirect support: throw
TemporaryRedirectExceptionorPermanentRedirectExceptionto drive redirects from your domain logic. - Fastify integration: a single plugin wraps Fastify’s error handler and converts exceptions into responses.
- Core-only helpers: use the exception model without Fastify in other Node.js frameworks.
Runtime requirements: Node.js
>= 24and Fastify^5.0.0. This is an ESM-only package.
- You must configure this plugin ahead of most other plugins, otherwise they may consume the exceptions themselves.
pnpm add fastify-http-exceptions fastifyRegister the plugin once, then throw HTTP exceptions from your routes:
import Fastify from 'fastify';
import { fastifyHttpExceptions } from 'fastify-http-exceptions';
import {
NotFoundException,
ForbiddenException,
} from 'fastify-http-exceptions/core';
const app = Fastify();
await app.register(fastifyHttpExceptions, {
// Optional: log unhandled (non-HTTPException) errors via fastify.log.error
logUnhandled: true,
});
app.get('/users/:id', async (request, reply) => {
const id = (request.params as { id: string }).id;
const user = await findUser(id);
if (!user) {
// Will become: 404 + { "error": "user not found" }
throw new NotFoundException('user');
}
if (!canAccessUser(request, user)) {
// Will become: 403 + { "error": "Access denied to user: missing permission" }
throw new ForbiddenException('user', 'missing permission');
}
return reply.send(user);
});You can also throw redirect exceptions and let the plugin turn them into Fastify redirect() calls:
import { TemporaryRedirectException } from 'fastify-http-exceptions/core';
app.get('/old-endpoint', async () => {
throw new TemporaryRedirectException('/new-endpoint');
});This results in a redirect response using the appropriate HTTP status code.
When an HTTPException (or compatible error) is thrown inside a Fastify route:
- Redirect exceptions (
TemporaryRedirectException,PermanentRedirectException) are translated into Fastify redirects. - For 4xx/5xx HTTP error codes, responses are shaped as:
{
"error": "Human-readable message"
}The conversion logic is handled by httpExceptionToResponse, which is used internally by the Fastify plugin.
If you want to reuse the typed HTTP exceptions without the Fastify plugin, import from the core entry:
import {
HTTPException,
NotFoundException,
ForbiddenException,
httpExceptionToResponse,
isHTTPException,
} from 'fastify-http-exceptions/core';
function getUserOrThrow(id: string) {
const user = findUserSync(id);
if (!user) {
throw new NotFoundException('user');
}
return user;
}
try {
const user = getUserOrThrow('123');
// ...
} catch (err) {
if (isHTTPException(err)) {
const response = httpExceptionToResponse(err);
// Integrate with your own HTTP server / framework here
} else {
// Fallback: log or convert to 500, etc.
}
}This works in any Node.js HTTP framework or even in pure Node HTTP servers.
The fastify-http-exceptions plugin accepts a small options object:
logUnhandled?: boolean- When
true, non-HTTPExceptionerrors are logged viafastify.log.errorbefore being passed to the original Fastify error handler. - Default:
false.
- When
Example:
await app.register(fastifyHttpExceptions, {
logUnhandled: true,
});This repository is a small monorepo:
packages/fastify-http-exceptions: core library and Fastify plugin.demos/basic-example: a demo Fastify app that uses the plugin.
You generally only need the published fastify-http-exceptions package, but the demo and tests live here as reference.
If you want to contribute or run the plugin locally:
# from the repo root
pnpm install
# type-check all packages
pnpm tsc
# run Biome checks (format + lint)
pnpm check
# build all packages
pnpm buildTo run the demo app:
pnpm dev # run all dev targets in parallel
pnpm start # start the basic-example demoYou can also work directly inside the package:
cd packages/fastify-http-exceptions
pnpm tsc # one-off type-check
pnpm dev # watch mode
pnpm vitest # run unit testsIMPORTANT: Always publish via the monorepo root using the release script, not via pnpm publish directly inside the package directory.
From the repository root:
pnpm run releaseThis will:
- Build the package.
- Copy
LICENSEand this rootREADME.mdinto the package’s publish folder. - Publish the package to npm with the correct metadata and README.
After publishing, remember to push commits and tags:
git push
git push --tagsThis plugin was born out of the practical need to standardize HTTP error handling in Fastify APIs:
- Application code should be able to express failures with simple, typed exceptions.
- The HTTP layer should consistently return structured JSON responses without repetitive boilerplate.
- The same exception model should be useful inside and outside of Fastify.
If you have suggestions, issues, or ideas for additional exception helpers, please open an issue or PR.