libflitter/errors/ErrorUnit.js

/**
 * @module libflitter/errors/ErrorUnit
 */

const Unit = require('../Unit')
const HTTPError = require('./HTTPError')

/**
 * The error unit defines last-resort handlers for HTTP 404 and HTTP
 * 500 errors. This unit must be loaded after all other routes. If it
 * isn't, it will preempt any other routes from working. The routes in
 * this unit are used if no other path is found. The handler alters the
 * status of the request to the HTTP error, then renders the appropriate
 * view in the "views/errors/" directory.
 * 
 * @extends module:libflitter/Unit~Unit
 */
class ErrorUnit extends Unit {
    /**
     * Defines the services required by this unit.
     * @returns {Array<string>}
     */
    static get services() {
        return [...super.services, 'output', 'views']
    }

    /**
     * Gets the name of the service provided by this unit: 'error_handling'
     * @returns {string} - 'error_handling'
     */
    static get name() {
        return 'error_handling'
    }

    /**
     * Loads the unit. Registers last-resort handlers for HTTP404 and HTTP500 errors with the underlying Express app.
     * All HTTP errors are handled by rendering the view where the canonical name is 'errors:CODE' where 'CODE' is the
     * numerical error code. If the 'server.environment' configuration is set to development, then all errors are handled
     * by rendering the 'errors:development' view.
     * @param {module:libflitter/app/FlitterApp~FlitterApp} app - the Flitter app
     * @returns {Promise<void>}
     */
    async go(app){

        /*
         * Tag 404 errors as a catch-all route.
         */
        app.express.use((req, res, next) => {
            let err = new HTTPError(404)
            this.output.error(`HTTP 404 - ${req.path}`)
            next(err)
        })

        /*
         * Handle HTTP errors by rendering the corresponding
         * view in views/errors/.
         */
        app.express.use((err, req, res, next) => {
            const status = err.status || 500
            res.status(status)
            this.output.error(`Internal Error (HTTP ${status} - ${req.path}) - ${err.message}`)
            return this.views.error(res, status, { error: err })
        })
    }
}

module.exports = exports = ErrorUnit