Middleware is a function which is called before the route handler. Middleware functions have access to oak's context object.
Middleware functions can perform the following tasks:
- execute any code.
- make changes to the context object.
You implement custom Danet middleware in either a function, or in a class with an @Injectable()
decorator. The class should implement the DanetMiddleware
interface, while the function does not have any special requirements. Let's start by implementing a simple middleware feature using the class method.
import { Injectable, DanetMiddle, HttpContext, NextFunction } from 'https://deno.land/x/danet/mod.ts';
@Injectable()
export class LoggerMiddleware implements DanetMiddleware {
async action(ctx: HttpContext, next: NextFunction) {
console.log('Request...');
await next();
}
}
Dependency injection
Danet middleware fully supports Dependency Injection. Just as with injectables and controllers, they are able to inject dependencies that are available within the same module. As usual, this is done through the constructor
.
Applying middleware
You can apply middlewares either globally, to controllers and to methods.
For global middlewares simply use addGlobalMiddlewares
DanetApplication's method as following:
...
const application = new DanetApplication();
await application.init(AppModule);
application.addGlobalMiddlewares(LoggerMiddleware); //as many middleware as you want;
...
For controllers and methods, simply use @Middleware
decorator ! Like addGlobalMiddlewares
, it can take as many middleware are you need for arguments.
@Middleware(LoggerMiddleware)
@Controller('todo')
class TodoController {
@Get('/')
getWithMiddleware() {
return 'OK'
}
};
Functional middleware
The LoggerMiddleware
class we've been using is quite simple. It has no members, no additional methods, and no dependencies. Why can't we just define it in a simple function instead of a class? In fact, we can. This type of middleware is called functional middleware. Let's transform the logger middleware from class-based into functional middleware to illustrate the difference:
import { Injectable, DanetMiddle, HttpContext, NextFunction } from 'https://deno.land/x/danet/mod.ts';
export async function logger(ctx: HttpContext, next: NextFunction) {
console.log(`Request...`);
await next();
};
And use it within the TodoController
:
@Middleware(logger)
@Controller('todo')
class TodoController {
@Get('/')
getWithMiddleware() {
return 'OK'
}
};
Hint
Consider using the simpler functional middleware alternative any time your middleware doesn't need any dependencies.
Multiple middleware
As mentioned above, in order to bind multiple middleware that are executed sequentially, simply provide them in left to right order to @Middleware
or addGlobalMiddleware
.
Global middleware
If we want to bind middleware to every registered route at once, simply use addGlobalMiddlewares
DanetApplication's method as following:
...
const application = new DanetApplication();
await application.init(AppModule);
application.addGlobalMiddlewares(YourFirstMiddleware, SecondMiddleware); //as many middleware as you want;
...