Middlewares
Introduction
Middleware provide a convenient mechanism for inspecting and filtering HTTP requests entering your application. For example, Athenna includes a middleware that verifies the user of your application is authenticated. If the user is not authenticated, the middleware will throw an unauthorized error. However, if the user is authenticated, the middleware will allow the request to proceed further into the application.
Additional middleware can be written to perform a variety of tasks besides authentication. For example, a logging middleware might log all incoming requests to your application.
Defining middleware
To create a new middleware, use the make:middleware
command:
node artisan make:middleware EnsureApiKeyIsValid
This command will place a new EnsureApiKeyIsValid
class within your app/Http/Middlewares
directory. In this middleware,
we will only allow access to the route if the supplied api key input matches a specified value. Otherwise, we will throw an
unauthorized exception:
import { UnauthorizedException } from '@athenna/http'
export class EnsureApiKeyIsValid {
/**
* Use the constructor to resolve any dependency of the Ioc container.
*/
constructor() {}
/**
* Handle method is executed before the request gets in your controller.
*
* @param {import('@athenna/http').HandleContextContract} ctx
* @return {any}
*/
async handle({ queries }) {
if (queries.apiKey !== 'my-api-key') {
throw new UnauthorizedException('Your api key does not match with application api key.')
}
}
}
As you can see, if the given apiKey
does not match our application api key, the middleware will return an unauthorized
exception to the client; otherwise, the request will be passed further into the application.
It's best to envision middleware as a series of "layers" HTTP requests must pass through before they hit your application. Each layer can examine the request and even reject it entirely.
tip
All middleware are resolved via the service container
, so you can use any
dependencies you need within a middlewares constructor.
Registering middleware
Global middleware
If you want a middleware to run during every HTTP request to your application, list the middleware class in the globalMiddlewares
property of your app/Http/Kernel.js
class.
Assigning middleware to routes
If you would like to assign middleware to specific routes, you should first assign the middleware a key in your application's
app/Http/Kernel.js
file. You can do that inside namedMiddlewares
property, you may add your own middleware to this list
and assign it a key of your choosing:
// Within app\Http\Kernel class...
get namedMiddlewares() {
return {
myMiddleware: import('App/Http/Middlewares/MyMiddleware')
}
}
Once the middleware has been defined in the HTTP kernel, you may use the middleware
method to assign middleware to a route:
Route.get('/welcome', () => {
//
}).middleware('myMiddleware')
Intercept middleware
Sometimes a middleware may need to do some work before the HTTP response has been sent to the client. If you define an
intercept
method on your middleware, the intercept
method will automatically be called before the response is sent to
the client:
export class InterceptMiddleware {
/**
* Use the constructor to resolve any dependency of the Ioc container.
*/
constructor() {}
/**
* Handle method is executed before the request gets in your controller.
*
* @param {import('@athenna/http').HandleContextContract} ctx
*/
async handle() {
}
/**
* Intercept method is executed before the response has been sent.
*
* @param {import('@athenna/http').InterceptContextContract} ctx
*/
async intercept({ body }) {
body.intercepted = true
// intercept should always return the new body of the response
return body
}
}
Once you have defined a intercept middleware, you should add it to the list of routes or global middleware in the app/Http/Kernel.js
file.
Terminate middleware
Sometimes a middleware may need to do some work after the HTTP response has been sent to the client. If you define a
terminate
method on your middleware, the terminate
method will automatically be called after the response is sent to
the client:
export class TerminateMiddleware {
/**
* Use the constructor to resolve any dependency of the Ioc container.
*/
constructor() {}
/**
* Handle method is executed before the request gets in your controller.
*
* @param {import('@athenna/http').HandleContextContract} ctx
*/
async handle() {
}
/**
* Terminate method is executed after the response has been sent.
*
* @param {import('@athenna/http').TerminateContextContract} ctx
*/
async terminate() {
// create and save metrics of the request
}
}
Once you have defined a terminable middleware, you should add it to the list of routes or global middleware in the app/Http/Kernel
file.
info
The requests log created by Athenna when the logRequests
property is true in your config/http.js
file are handled by a
terminable middleware! You can see the code here.