Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

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 methodWhat it does
debugIn development shows a detailed error page with the stack trace
renderStatusPagesOn the server turns on nice error pages
statusPagesWhich template to show for which status
handleBuilds the response to the visitor
reportWrites 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>
@end

The 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.

Thanks for reading!

Donate to the site — we work hard for you.