Error handling
Errors happen: a post is not found, a user lacks permission, the database is down. AdonisJS catches every exception thrown from routes and controllers in one place — the global handler. It decides what to show the visitor and what to write to the log.
The handler
The file app/exceptions/handler.ts already exists in the project:
// app/exceptions/handler.ts
import app from '@adonisjs/core/services/app'
import { HttpContext, ExceptionHandler } from '@adonisjs/core/http'
import type { StatusPageRange, StatusPageRenderer } from '@adonisjs/core/types/http'
export default class HttpExceptionHandler extends ExceptionHandler {
protected debug = !app.inProduction
protected renderStatusPages = app.inProduction
protected statusPages: Record<StatusPageRange, StatusPageRenderer> = {
'404': (error, { view }) => view.render('pages/errors/not_found', { error }),
'500..599': (error, { view }) => view.render('pages/errors/server_error', { error }),
}
async handle(error: unknown, ctx: HttpContext) {
return super.handle(error, ctx)
}
async report(error: unknown, ctx: HttpContext) {
return super.report(error, ctx)
}
}| Property or method | What it does |
|---|---|
debug | In development shows a detailed error page with the stack trace |
renderStatusPages | On the server turns on nice error pages |
statusPages | Which template to show for which status |
handle | Builds the response to the visitor |
report | Writes the error to the log |
The 404 page
{{-- resources/views/pages/errors/not_found.edge --}}
@layout({ title: 'Page not found' })
<h1>This page does not exist</h1>
<p><a href="{{ route('posts.index') }}">Back to the posts</a></p>
@endThe template is shown for any 404 response, for example when Post.findOrFail does not find the post. In development you see the debug page instead; to check yours, temporarily set renderStatusPages = true.
A custom exception
node ace make:exception PostLocked// app/exceptions/post_locked_exception.ts
import { Exception } from '@adonisjs/core/exceptions'
export default class PostLockedException extends Exception {
static status = 423
static code = 'E_POST_LOCKED'
static message = 'The post is closed for comments'
}// app/controllers/comments_controller.ts
import PostLockedException from '#exceptions/post_locked_exception'
if (post.isLocked) {
throw new PostLockedException()
}The handler returns status 423 and the message text: as JSON for a JSON client, as a page for a browser.
A custom response for an exception
An exception can decide how to respond itself — declare a handle method in it:
// app/exceptions/post_locked_exception.ts
async handle(error: this, ctx: HttpContext) {
ctx.session.flash('error', error.message)
return ctx.response.redirect().back()
}Then the visitor goes back to the post page and sees a flash message. Import HttpContext from @adonisjs/core/http.
Common mistake
On the server people forget to set NODE_ENV=production. Then app.inProduction is false, debug is on, and on any error the visitor sees the stack trace, file paths and pieces of code. Check NODE_ENV in the production server's .env.