Middleware
Introduction
The concept of middleware in Lunox is the same as Laravel Middleware. Middleware runs before the main logic of your application. For example, you can filter user requests before they enter your application, check cookies or sessions, and more.
Defining Middleware
All middleware files are located in the app/Middleware
folder.
Use the artisan command to create middleware:
pnpm artisan make:middleware Auth
Object-Based and Class-Based Middleware
Middleware can be plain objects or class-based. For simple middleware, use plain objects. Here's an example of middleware using a plain object:
import type { Middleware } from "@lunoxjs/core/contracts";
const AuthMiddleware: Middleware = {
async handle(req, next) {
// Perform authentication here
if (!(await req.auth().check())) {
throw new ApiException("Please login", 401);
}
return next(req);
},
};
export default AuthMiddleware;
And here's an equivalent Auth middleware using a class:
import type { Middleware } from "@lunoxjs/core/contracts";
class AuthMiddleware implements Middleware {
async handle(req, next) {
// Perform authentication here
if (!(await req.auth().check())) {
throw new ApiException("Please login", 401);
}
return next(req);
}
}
export default AuthMiddleware;
Middleware Types
There are three types of middleware: Before Middleware, After Middleware, and Native Middleware. Usually, you will only create Before Middleware.
Before Middleware
Before middleware is middleware that runs before the route action is executed. For example, middleware that handles user authentication. To create before middleware, simply create a handle
method. See this example:
class AuthMiddleware implements Middleware {
async handle(req, next) {
// Perform authentication here
if (!(await req.auth().check())) {
throw new ApiException("Please login", 401);
}
return next(req);
}
}
Next Method
The next
method can accept one argument, which is the Http/Request
instance. This ensures that the request instance is updated in the next step. See the example above.
Before middleware must return an instance of the Lunox Http/Response
. The return type of the next
function is Http/Response
.
After Middleware
Sometimes we want to perform some actions after the route action is executed but before the response is sent to the browser. After middleware exists to handle that situation. For example, the Lunox EncryptCookie
middleware uses before middleware to decrypt the incoming cookie and after middleware to encrypt it back. Simply create a handleAfter
method to implement after middleware.
class EncryptCookie implements Middleware {
async handleAfter(res) {
// Perform encryption here
res = this.encrypt(res);
return res;
}
}
Similar to Before Middleware, After Middleware must return a Lunox Http/Response
instance. The difference is that the parameter of the handleAfter
method is an instance of Http/Response
instead of Http/Request
and NextFunction
.
Native Middleware
Due to the large community of Node.js, there are many middleware packages that are supported for the express
and polka
frameworks. Lunox is built on top of polka
, so you can use those packages within a Lunox app. For example, to handle CORS, you can implement this kind
of middleware using Native Middleware. Simply create a handleNative
method inside your middleware.
// In an Express or Polka application
import cors from "cors";
// In Lunox
const CorsMiddleware: Middleware = {
async handleNative(req, res, next) {
return cors({
// ...config
})(req, res, next);
},
};
The req
, res
, and next
parameters of the handleNative
method are instances of ServerRequest
, ServerResponse
, and NextFunction
of the Polka HTTP server. They are also suitable for express
middleware packages.
Registering Middleware
Middleware is registered in app/Http/Kernel
. You can register your custom middleware in three different types in the HTTP Kernel.
class Kernel extends BaseKernel {
// Global middleware
protected middleware = [CorsMiddleware];
// Group middleware
protected middlewareGroups = {
web: [StartSession],
};
// Route middleware
protected routeMiddleware = {
auth: AuthMiddleware,
session: SessionMiddleware,
};
}
export default Kernel;
Global Middleware
This middleware runs on every request made. So if you want to include the CORS middleware, this is the appropriate place.
Group Middleware
You can group two or more middlewares into one group and then assign this group name to some routes. For example, the web
group middleware. See app/Providers/RouteServiceProvider.ts
to see how to assign the web
group middleware.
class RouteServiceProvider extends ServiceProvider {
async register() {}
async boot() {
await Route.middleware("web") //<-- Here we assign the web group middleware to web-based routes.
.group(base_path("routes/web"));
await Route.prefix("/api").group(base_path("routes/api"));
}
}
Route Middleware
This is a key-value pair of middleware (aliasing middleware). Route middleware can be assigned to any routes.
Route.get("/someuri", () => "OK").middleware("auth");
// or group of routes
// remember group() method return Promise so we add await here
await Route.middleware("auth").group(() => {
// All routes in this group will use the auth middleware
Route.get("/someuri", () => "OK");
Route.get("/another", () => "OK");
});
The Route.group
method is asynchronous, so make sure to await
this method.
The Route.middleware
method can accept an array of middleware. So you can do something like this:
Route.get("/someurl", () => "OK").middleware(["auth", "admin"]);
Middleware Params
The handle
method of Middleware can have parameters. We can access them using the following example:
class ExampleMiddleware implements Middleware {
async handle(req, next, param1, param2) {
// Use the params here
console.log(param1, param2);
return next(req);
}
}
In the above code, the handle
method of the middleware accepts param1
and param2
as additional parameters.
Then, in the route, we can set the params after the middleware name followed by ":" as shown below:
Route.get("/example", () => "OK").middleware("example:param1,param2");
In the route definition above, the middleware named "example" is applied, and the values "param1" and "param2" are passed as parameters to the middleware.
Route.withoutMiddleware
method to exclude middleware on some routes.