localhost
GET
This page provide a more advance guide for effectively handling errors with Elysia.
If you haven't read "Life Cycle (onError)" yet, we recommend you to read it first.
When defining a schema, you can provide a custom validation message for each field.
This message will be returned as-is when the validation fails.
import { Elysia } from 'elysia'
new Elysia().get('/:id', ({ params: { id } }) => id, {
params: t.Object({
id: t.Number({
error: 'id must be a number'
})
})
})
If the validation fails on the id
field, the response will be return as id must be a number
.
GET
Returning as value from schema.error
will return the validation as-is, but sometimes you may also want to return the validation details, such as the field name and the expected type
You can do this by using the validationDetail
option.
import { Elysia, validationDetail } from 'elysia'
new Elysia().get('/:id', ({ params: { id } }) => id, {
params: t.Object({
id: t.Number({
error: validationDetail('id must be a number')
})
})
})
This will include all of the validation details in the response, such as the field name and the expected type.
GET
But if you're planned to use validationDetail
in every field, adding it manually can be annoying.
You can automatically add validation detail by handling it in onError
hook.
new Elysia()
.onError(({ error, code }) => {
if (code === 'VALIDATION') return error.detail(error.message)
})
.get('/:id', ({ params: { id } }) => id, {
params: t.Object({
id: t.Number({
error: 'id must be a number'
})
})
})
.listen(3000)
This will apply every validation error with a custom message with custom validation message.
By default, Elysia will omitted all validation detail if NODE_ENV
is production
.
This is done to prevent leaking sensitive information about the validation schema, such as field names and expected types, which could be exploited by an attacker.
Elysia will only return that validation failed without any details.
{
"type": "validation",
"on": "body",
"found": {},
// Only shown for custom error
"message": "x must be a number"
}
The message
property is optional and is omitted by default unless you provide a custom error message in the schema.
Elysia supports custom error both in the type-level and implementation level.
By default, Elysia have a set of built-in error types like VALIDATION
, NOT_FOUND
which will narrow down the type automatically.
If Elysia doesn't know the error, the error code will be UNKNOWN
with default status of 500
But you can also add a custom error with type safety with Elysia.error
which will help narrow down the error type for full type safety with auto-complete, and custom status code as follows:
import { Elysia } from 'elysia'
class MyError extends Error {
constructor(public message: string) {
super(message)
}
}
new Elysia()
.error({
MyError
})
.onError(({ code, error }) => {
switch (code) {
// With auto-completion
case 'MyError':
// With type narrowing
// Hover to see error is typed as `CustomError`
return error
}
})
.get('/:id', () => {
throw new MyError('Hello Error')
})
You can also provide a custom status code for your custom error by adding status
property in your custom error class.
import { Elysia } from 'elysia'
class MyError extends Error {
status = 418
constructor(public message: string) {
super(message)
}
}
Elysia will then use this status code when the error is thrown.
Otherwise you can also set the status code manually in the onError
hook.
import { Elysia } from 'elysia'
class MyError extends Error {
constructor(public message: string) {
super(message)
}
}
new Elysia()
.error({
MyError
})
.onError(({ code, error, status }) => {
switch (code) {
case 'MyError':
return status(418, error.message)
}
})
.get('/:id', () => {
throw new MyError('Hello Error')
})
You can also provide a custom toResposne
method in your custom error class to return a custom response when the error is thrown.
import { Elysia } from 'elysia'
class MyError extends Error {
status = 418
constructor(public message: string) {
super(message)
}
toResponse() {
return Response.json({
error: this.message,
code: this.status
}, {
status: 418
})
}
}
Most of an error handling in Elysia can be done by throwing an error and will be handle in onError
.
But for status
it can be a little bit confusing, since it can be used both as a return value or throw an error.
It could either be return or throw based on your specific needs.
status
is throw, it will be caught by onError
middleware.status
is return, it will be NOT caught by onError
middleware.See the following code:
import { Elysia, file } from 'elysia'
new Elysia()
.onError(({ code, error, path }) => {
if (code === 418) return 'caught'
})
.get('/throw', ({ status }) => {
// This will be caught by onError
throw status(418)
})
.get('/return', ({ status }) => {
// This will NOT be caught by onError
return status(418)
})
GET