Exception handling
Exceptions raised during an HTTP request are handled by the HttpExceptionHandler
defined inside the ./app/exceptions/handler.ts
file. Inside this file, you can decide how to convert exceptions to responses and log them using the logger or report them to an external logging provider.
The HttpExceptionHandler
extends the ExceptionHandler class, which does all the heavy lifting of handling errors and provides you with high-level APIs to tweak the reporting and rendering behavior.
import app from '@adonisjs/core/services/app'
import { HttpContext, ExceptionHandler } from '@adonisjs/core/http'
export default class HttpExceptionHandler extends ExceptionHandler {
protected debug = !app.inProduction
protected renderStatusPages = app.inProduction
async handle(error: unknown, ctx: HttpContext) {
return super.handle(error, ctx)
}
async report(error: unknown, ctx: HttpContext) {
return super.report(error, ctx)
}
}
Assigning error handler to the server
The error handler is registered with the AdonisJS HTTP server inside the start/kernel.ts
file. We lazily import the HTTP handler using the #exceptions
alias defined in the package.json
file.
server.errorHandler(() => import('#exceptions/handler'))
Handling exceptions
The exceptions are handled by the handle
method on the exceptions handler class. By default, the following steps are performed while handling an error.
- Check if the error instance has a
handle
method. If yes, call the error.handle method and return its response. - Check if a status page is defined for the
error.status
code. If yes, render the status page. - Otherwise, render the exception using content negotiation renderers.
If you want to handle a specific exception differently, you can do that inside the handle
method. Make sure to use the ctx.response.send
method to send a response, since the return value from the handle
method is discarded.
import { errors } from '@vinejs/vine'
export default class HttpExceptionHandler extends ExceptionHandler {
async handle(error: unknown, ctx: HttpContext) {
if (error instanceof errors.E_VALIDATION_ERROR) {
ctx.response.status(422).send(error.messages)
return
}
return super.handle(error, ctx)
}
}
Status pages
Status pages are a collection of templates you want to render for a given or a range of status codes.
The range of status codes can be defined as a string expression. Two dots separate the starting and the ending status codes (..
).
If you are creating a JSON server, you may not need status pages.
import { StatusPageRange, StatusPageRenderer } from '@adonisjs/http-server/types'
export default class HttpExceptionHandler extends ExceptionHandler {
protected statusPages: Record<StatusPageRange, StatusPageRenderer> = {
'404': (_, { view }) => view.render('errors/not-found'),
'500..599': (_, { view }) => view.render('errors/server-error')
}
}
Debug mode
The content negotiation renderers handle exceptions that are not self-handled and not converted to a status page.
The content negotiation renderers have support for debug mode. They can parse and pretty-print errors in debug mode using the Youch npm package.
You can toggle the debug mode using the debug
property on the exceptions handler class. However, turning off the debug mode in production is recommended, as it exposes sensitive information about your app.
export default class HttpExceptionHandler extends ExceptionHandler {
protected debug = !app.inProduction
}
Reporting exceptions
The report
method on the exceptions handler class handles reporting of exceptions.
The method receives the error as the first argument and the HTTP context as the second argument. You should not write a response from the report
method and use the context only to read the request information.
Logging exceptions
All exceptions are reported using the logger by default.
- Exceptions with status codes in the
400..499
range are logged in thewarning
level. - Exceptions with the status code
>=500
are logged in theerror
level. - All other exceptions are logged in the
info
level.
You can add custom properties to the log messages by returning an object from the context
method.
export default class HttpExceptionHandler extends ExceptionHandler {
protected context(ctx: HttpContext) {
return {
requestId: ctx.requestId,
userId: ctx.auth.user?.id,
ip: ctx.request.ip(),
}
}
}
Ignoring status codes
You can ignore exceptions from being reported by defining an array of status codes via the ignoreStatuses
property.
export default class HttpExceptionHandler extends ExceptionHandler {
protected ignoreStatuses = [
401,
400,
422,
403,
]
}
Ignoring errors
You can also ignore exceptions by defining an array of error codes or error classes to ignore.
import { errors } from '@adonisjs/core'
import { errors as sessionErrors } from '@adonisjs/session'
export default class HttpExceptionHandler extends ExceptionHandler {
protected ignoreCodes = [
'E_ROUTE_NOT_FOUND',
'E_INVALID_SESSION'
]
}
An array of exception classes can be ignored using the ignoreExceptions
property.
import { errors } from '@adonisjs/core'
import { errors as sessionErrors } from '@adonisjs/session'
export default class HttpExceptionHandler extends ExceptionHandler {
protected ignoreExceptions = [
errors.E_ROUTE_NOT_FOUND,
sessionErrors.E_INVALID_SESSION,
]
}
Custom shouldReport method
The logic to ignore status codes or exceptions is written inside the shouldReport
method. If needed, you can override this method and define your custom logic for ignoring exceptions.
import { HttpError } from '@adonisjs/core/types/http'
export default class HttpExceptionHandler extends ExceptionHandler {
protected shouldReport(error: HttpError) {
// return a boolean
}
}
Custom exceptions
You can create an exception class using the make:exception
ace command. An exception extends the Exception
class from the @adonisjs/core
package.
See also: Make exception command
node ace make:exception UnAuthorized
import { Exception } from '@adonisjs/core/exceptions'
export default class UnAuthorizedException extends Exception {}
You can raise the exception by creating a new instance of it. When raising the exception, you can assign a custom error code and status code to the exception.
import UnAuthorizedException from '#exceptions/unauthorized_exception'
throw new UnAuthorizedException('You are not authorized', {
status: 403,
code: 'E_UNAUTHORIZED'
})
The error and status codes can also be defined as static properties on the exception class. The static values will be used if no custom value is defined when throwing the exception.
import { Exception } from '@adonisjs/core/exceptions'
export default class UnAuthorizedException extends Exception {
static status = 403
static code = 'E_UNAUTHORIZED'
}
Defining the handle
method
To self-handle the exception, you can define the handle
method on the exception class. This method should convert an error to an HTTP response using the ctx.response.send
method.
The error.handle
method receives an instance of the error as the first argument and the HTTP context as the second argument.
import { Exception } from '@adonisjs/core/exceptions'
import { HttpContext } from '@adonisjs/core/http'
export default class UnAuthorizedException extends Exception {
async handle(error: this, ctx: HttpContext) {
ctx.response.status(error.status).send(error.message)
}
}
Define the report
method
You can implement the report
method on the exception class to self-handle the exception reporting. The report method receives an instance of the error as the first argument and the HTTP context as the second argument.
import { Exception } from '@adonisjs/core/exceptions'
import { HttpContext } from '@adonisjs/core/http'
export default class UnAuthorizedException extends Exception {
async report(error: this, ctx: HttpContext) {
ctx.logger.error({ err: error }, error.message)
}
}
Narrowing down the error type
The framework core and other official packages exports the exceptions raised by them. You can verify if an error is an instance of a specific exception using the instanceof
check. For example:
import { errors } from '@adonisjs/core'
try {
router.builder().make('articles.index')
} catch (error: unknown) {
if (error instanceof errors.E_CANNOT_LOOKUP_ROUTE) {
// handle error
}
}
Known errors
Please check the exceptions reference guide to view the list of known errors.