Middlewares
Understand how you can set up middlewares in your REST API application.
Introduction
Middleware provides 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
Path.middlewares()
./src/http/middlewares
import { Context, Middleware, MiddlewareContract } from '@athenna/http'
@Middleware()
export class EnsureApiKeyIsValid implements MiddlewareContract {
public async handle(({ request }): Context): Promise<void> {
if (request.query('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.
All middleware is resolved via the service container
,
so you can use any dependencies you need within a
middlewares constructor.
Registering middleware
If you are using make:middleware
command to register your
middlewares, Athenna will auto register your middlewares
inside the middlewares
property of your .athennarc.json
file:
{
"middlewares": [
"#src/http/middlewares/EnsureApiKeyIsValid"
]
}
Remember that if you have not created your middleware using
make:middleware
command, you will need to manually add it
to middlewares
array. If you don't do that, Athenna will
not register your middleware in the service container
and you will not be able to use it.
Global middleware
If you want a middleware to run during every HTTP request
to your application, you can set the isGlobal
value to
true in the @Middleware()
annotation:
import { Middleware } from '@athenna/http'
import type { Context, MiddlewareContract } from '@athenna/http'
@Middleware({ isGlobal: true }) 👈
export class EnsureApiKeyIsValid implements MiddlewareContract {
public async handle(({ request }): Context): Promise<void> {
if (request.query('apiKey') !== 'my-api-key') {
throw new UnauthorizedException('Your api key does not match with application api key.')
}
}
}
If you are not using TypeScript, you can use the
globalMiddlewares
array in your .athennarc.json
file:
{
"globalMiddlewares": [
"#src/http/middlewares/EnsureApiKeyIsValid"
]
}
Assigning middleware to routes
If you would like to assign middleware to specific routes,
you can use the name of your middleware class in camelCase
(ensureApiKeyIsValid
). But we recommend you to assign a
custom name to your middleware using the @Middleware()
annotation:
import { Middleware } from '@athenna/http'
import type { Context, MiddlewareContract } from '@athenna/http'
@Middleware({ name: 'auth' }) 👈
export class EnsureApiKeyIsValid implements MiddlewareContract {
public async handle(({ request }): Context): Promise<void> {
if (request.query('apiKey') !== 'my-api-key') {
throw new UnauthorizedException('Your api key does not match with application api key.')
}
}
}
If you are not using TypeScript, you can use the
namedMiddlewares
object in your .athennarc.json
file:
{
"namedMiddlewares": {
"auth": "#src/http/middlewares/EnsureApiKeyIsValid"
}
}
Once the middleware name has been defined, you may use the
Route.middleware()
method to assign middleware to a route:
Route.get('/welcome', () => {
//
}).middleware('auth') 👈
To learn more about the @Middleware()
annotation, visit the
REST API annotations documentation page.
Intercept middleware
Sometimes a middleware may need to do some work before the HTTP response has been sent to the client. For this purpose, you can use the Athenna interceptor middleware. To create one, you can use the following command:
node artisan make:interceptor InterceptMiddleware
Just like make:middleware
, Athenna will auto register the
middleware if using make:interceptor
command. The above
command will produce the following content at
src/http/interceptors/InterceptMiddleware.ts
file:
import { Interceptor } from '@athenna/http'
import type { InterceptContext, InterceptorContract } from '@athenna/http'
@Interceptor()
export class InterceptMiddleware implements InterceptorContract {
public async intercept(({ response }): InterceptContext): Promise<unknown> {
response.body.intercepted = true
return response.body
}
}
As you can see in the example above, you should always return the intercepted response body in interceptors.
The registration process of Athenna interceptors is the same of middlewares.
To assign your interceptor to a route, you can use the
Route.interceptor()
method:
Route.get('/welcome', () => {
//
}).interceptor('interceptMiddleware') 👈
To learn more about the @Interceptor()
annotation, visit the
REST API annotations documentation page.
Terminate middleware
Sometimes a middleware may need to do some work after the HTTP response has been sent to the client. For this purpose, you can use the Athenna interceptor middleware. To create one, you can use the following command:
node artisan make:terminator TerminateMiddleware
Just like make:middleware
, Athenna will auto register the
middleware if using make:terminator
command. The above
command will produce the following content at
src/http/terminators/TerminateMiddleware.ts
file:
import { Terminator } from '@athenna/http'
import type { TerminateContext, TerminatorContract } from '@athenna/http'
@Terminator()
export class TerminateMiddleware implements TerminatorContract {
public async terminate(ctx: TerminateContext): Promise<void> {}
}
The registration process of Athenna terminators is the same of middlewares.
To assign your terminator to a route, you can use the
Route.terminator()
method:
Route.get('/welcome', () => {
//
}).terminator('terminateMiddleware') 👈
The requests log created by Athenna when the logger
property is true in your
Path.config('http.ts')
./src/config/http.ts
To learn more about the @Terminator()
annotation, visit the
REST API annotations documentation page.