Error handling
If a handler throws an error, Fastify catches it and answers the client with JSON and an error code. Your job is to throw errors with the right code and, if you like, to set up how they look.
An error with a code
Fastify takes the status code from the error's statusCode property. Without it you get 500:
// src/routes/posts.js
app.get('/posts/:id', async (request) => {
const post = app.posts.find((item) => item.id === request.params.id);
if (!post) {
const error = new Error('Post not found');
error.statusCode = 404;
throw error;
}
return post;
});/posts/99 returns {"statusCode":404,"error":"Not Found","message":"Post not found"}.
@fastify/sensible
Creating an error by hand is long. The official @fastify/sensible plugin adds ready-made functions:
npm install @fastify/sensible// src/app.js
import sensible from '@fastify/sensible';
app.register(sensible);Now app.httpErrors is available in any route:
// src/routes/posts.js
app.get('/posts/:id', async (request) => {
const post = app.posts.find((item) => item.id === request.params.id);
if (!post) {
throw app.httpErrors.notFound('Post not found');
}
return post;
});The reply is the same and the code is shorter. There are others: badRequest() — 400, unauthorized() — 401, forbidden() — 403, conflict() — 409.
Your own error handler
setErrorHandler sets a function every error goes through. Add to src/app.js:
// src/app.js
app.setErrorHandler((error, request, reply) => {
const statusCode = error.statusCode ?? 500;
if (statusCode >= 500) {
request.log.error(error);
return reply.code(500).send({ error: 'Internal server error' });
}
return reply.code(statusCode).send({ error: error.message });
});Client errors (4xx codes) are returned with their original message, while server errors are logged and sent to the client without details. Schema validation errors come here too — with code 400 and an error.validation property.
The 404 page
Addresses without a route have a separate handler:
// src/app.js
app.setNotFoundHandler((request, reply) => {
return reply.code(404).send({ error: `Address ${request.url} not found` });
});http://localhost:3000/nothing returns {"error":"Address /nothing not found"}.
Where the handlers apply
Both methods follow encapsulation: called inside a plugin, they apply only to its routes. For example, the /admin section can have its own 404 page.
Common mistake
Sending error.message to the client for every error is dangerous: a database or code error message can reveal table names, file paths and other details. For code 500 answer with a general phrase and write the details to the log, as in the example above.