Middleware

Introduction

Middlewares are an essential mechanism in your application to filter HTTP requests. A common example is an authentication middleware letting requests proceed the request lifecycle which meet the authentication requirements. In contrast, a request that is unauthenticated will be redirected to the login screen.

Based on certain conditions, requests may proceed or terminate early in the request lifecycle.

Middleware in Supercharge

Middleware in Supercharge are located in the app/middleware directory. This directory does not exist with a default installation. Go ahead and create this directory when adding your own middleware.

Request Lifecycle

Supercharge uses hapi, a Node.js web framework, as the HTTP layer. A benefit of using hapi is the extensive request lifecycle.

Extension Points

Supercharge provides seven extension points along the request lifecycle:

  • onRequest
  • onPreAuth
  • onCredentials
  • onPostAuth
  • onPreHandler
  • onPostHandler
  • onPreResponse

Middleware in Supercharge must extend one of the listed extension points. Notice that you have five extension points before hitting the route handler and two after the route handler. Every response (no matter if success or failure) will go through the onPreResponse extension point. This is the right place for use cases where you want to intercept all your responses.

Request Lifecycle Cheat Sheet

We’ve created a request lifecycle cheat sheet outlining every step that a request goes along the way through the framework. The link redirects you to the PDF download page.

Creating Middleware

A middleware in Supercharge is a so called “lifecycle function” with the signature async (request, h). The term lifecycle function is related to being a function in the request lifecycle.

Create a Supercharge middleware by exporting an object with two properties:

  1. type: must be one of the listed extension points
  2. method: the lifecycle method
module.exports = {
  type: 'onPostAuth',
  method: async (request, h) => {
    // your middleware code

    // here’s an example:
    if (request.auth.isAuthenticated) {
      console.log(`Authenticated user passing by: ${request.user().id}`)
    }
  }
}

Middlewares can remove duplicated code from your route handlers. Remember global and route-level middleware whenever you think that functionality should live outside of route handlers.

Using Middleware

Supercharge automatically loads all your middleware when starting the application. It’ll recursively traverse the app/middleware folder, pick up all files and load it as individual middleware to your app.

Registering Global Middleware

Every file located in app/middleware is a global middleware. Precisely, every request to your application will go through all middleware located in the app/middleware.

Registering Middleware on Routes

You can create middleware and assign it to individual routes. In this case, you should locate the route-level middleware next to your route file. To tell Supercharge to skip your route-level middleware when loading route file (because it’s not a route), prefix the file with an underscore _.

Skipping middleware files isn’t available in Supercharge yet. That’s the reason you have to put route-level middleware in the routes folder. If you want to PR this feature, please don’t hesitate!

Example

Imagine an application rendering the docs you’re currently reading. A file tree in the application may look like this:

app
routes
docs
page.js
_check-version-middleware.js

Remember that _check-version-middleware.js is prefixed with an underscore telling Supercharge to skip this file when loading routes.

The _check-version-middleware.js then exports the middleware’s lifecycle function:

async function checkVersion(request, h) {
  // check docs version
}

module.exports = checkVersion

The next step is to assign this middleware in your documation’s page.js route:

module.exports = {
  method: 'GET',
  path: '/docs/{{page}}',
  options: {
    ext: {
      onPreHandler: {
        method: require('./_check-version-middleware')
      }
    }
    handler: (request, h) => {
      // render docs page
    })
  }
}

Requests to GET /docs/{{page}} will go through the version check middleware at the onPreHandler extension point.