Error Handling in feTS
In feTS, there are several methods available to handle errors effectively.
Utilizing HTTP Status Codes (Recommended)
It is suggested to use HTTP status codes for error handling in lieu of throwing errors. When an
error is anticipated, it is beneficial to catch them using try/catch
blocks and return a response
with a specific status code.
This approach allows you to also take advantage of response schemas. If multiple status codes are defined in the response schema for a specific endpoint, feTS enforces that only those status codes can be returned within the handler. Additionally, it provides type-checking for the response body associated with each status code.
Here is a sample use case:
import { App } from 'uWebSockets.js';
import { createRouter, FromSchema } from 'fets';
const users = [
{ id: "1", name: 'John Doe' },
{ id: "2", name: 'Jane Doe' },
];
const router = createRouter().route({
method: 'GET',
path: '/user/:id',
schemas: {
request: {
params: {
type: 'object',
properties: {
id: { type: 'string' }
},
required: ['id']
additionalProperties: false
}
},
responses: {
200: {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' }
},
required: ['id', 'name'],
additionalProperties: false,
},
404: {
type: 'object',
properties: {
message: { type: 'string' }
},
required: ['message'],
additionalProperties: false,
}
}
},
handler: ({ params }) => {
const user = users.find(user => user.id === params.id);
if (!user) {
return Response.json({ message: 'User not found' }, { status: 404 });
}
if (user.name === 'Jane Doe') {
// @ts-expect-error - 401 is not defined in the response schema
return Response.json({ message: 'Jane is not allowed'}, { status: 401 });
}
if (user.name === 'John Doe') {
// @ts-expect-error - 200 response schema doesn't have a `surname` property
return Response.json({ id: user.id, name: user.name, surname: 'Doe' });
}
return Response.json(user);
}
});
App().any('/*', router).listen(3000, () => {
console.log(`Swagger UI is available at http://localhost:3000/docs`)
});
Throwing Errors
When an unexpected error is thrown, the response will have a 500
status code. Although it is
highly recommended to handle errors inside the handlers using try/catch
blocks, you can also use
the error handling plugin to catch errors.
Error Handling
feTS allows you to handle all errors thrown in the handlers. This way, you can prevent exposing internal errors, such as database errors that might contain sensitive information, to the consumer.
Here’s an example:
import { createRouter } from 'fets'
const router = createRouter({
onError(error, request, context) {
if (error.name === 'MyError') {
return Response.json(
{
message: error.message
},
{
status: 400
}
)
}
console.error(`Unexpected error in ${request.method} ${request.url}:\n${error.stack}`)
return Response.json(
{
message: 'Something went wrong'
},
{
status: 500
}
)
}
}).route({
path: '/users',
method: 'GET',
handler: async () => {
throw new Error('Something went wrong')
}
})
Utilizing HTTPError
In certain scenarios, you might find it helpful to throw an HTTPError
instead of returning a
Response
. This can be particularly useful when you are unable to return a Response
in some parts
of your code.
The HTTPError
can be thrown by specifying the status code, message, headers, and body. If you pass
a JSON object as the body, the response will be formatted as a JSON response.
Here’s an example of how to use HTTPError
:
import { createRouter, HTTPError } from 'fets'
import { HTTPError } from 'fets'
const router = createRouter().route({
path: '/me',
method: 'GET',
schemas: {
responses: {
200: {
type: 'object',
properties: {
name: { type: 'string' }
}
}
}
} ,
handler: request => {
if (!request.headers.get('Authorization')) {
// The `HTTPError` can be used to return a custom error response.
// The function accepts a status code, a message, headers, and a body.
throw new HTTPError(
401,
'Unauthorized',
{
'WWW-Authenticate': 'Basic'
},
{
message: 'You need to be authenticated to access this resource.'
}
)
}
}
})