Skip to content

HTTP Middleware ​

HTTP Middleware classes inspect, filter, and preprocess HTTP requests entering your application before they reach Controller action methods.


Creating Middleware ​

Use the StruxJS CLI to generate a new Middleware class in app/Middleware/:

bash
npx strux make:middleware AuthMiddleware

Generated file (app/Middleware/AuthMiddleware.ts):

typescript
import { Middleware, Request, Response } from "struxjs";

export class AuthMiddleware implements Middleware {
    public async handle(request: Request, response: Response, ...params: string[]) {
        // Perform pre-request inspection or authentication logic
    }
}

Middleware Implementation ​

A Middleware class implements the Middleware interface and defines a handle method:

typescript
import { Middleware, Request, Response } from "struxjs";

export class AuthMiddleware implements Middleware {
    public async handle(request: Request, response: Response, role?: string) {
        const token = request.header("authorization");

        if (!token) {
            return response.status(401).json({ error: "Unauthorized access" });
        }

        if (role && role !== "admin") {
            return response.status(403).json({ error: "Insufficient permissions" });
        }
    }
}

If the middleware passes checks, execution automatically continues to the next middleware or Controller action. If a response method (response.json(), response.redirect(), etc.) is called, request processing halts immediately.


Registering String Aliases ​

Register custom string aliases or binding tokens in app/Providers/AppServiceProvider.ts:

typescript
// app/Providers/AppServiceProvider.ts
import { Container } from "struxjs";
import { AuthMiddleware } from "../Middleware/AuthMiddleware.js";
import { CheckRoleMiddleware } from "../Middleware/CheckRoleMiddleware.js";

export class AppServiceProvider {
    public register(container: Container): void {
        // Register string aliases for Middlewares
        container.bind("auth", (c) => c.make(AuthMiddleware));
        container.bind("custom-auth", (c) => c.make(AuthMiddleware));
        container.bind("role", (c) => c.make(CheckRoleMiddleware));
    }
}

Attaching Middleware to Routes ​

Attach middleware to individual routes using .middleware() or .use():

typescript
import { Route } from "struxjs";
import { AuthMiddleware } from "../app/Middleware/AuthMiddleware.js";
import { UserController } from "../app/Controllers/UserController.js";

Route.get("/profile", [UserController, "profile"])
    .middleware(AuthMiddleware);

2. Array of Class References ​

typescript
import { AuthMiddleware } from "../app/Middleware/AuthMiddleware.js";
import { CheckRoleMiddleware } from "../app/Middleware/CheckRoleMiddleware.js";

Route.get("/admin", [UserController, "admin"])
    .middleware([AuthMiddleware, CheckRoleMiddleware]);

3. String Alias ​

typescript
Route.get("/dashboard", "UserController@dashboard")
    .middleware("auth");

4. String Alias with Parameters ​

Pass parameters to middleware using colon syntax ("alias:param1,param2"):

typescript
Route.get("/admin/settings", "AdminController@settings")
    .middleware("role:admin,superadmin");

In the middleware's handle method, these parameters are received after request and response:

typescript
export class CheckRoleMiddleware implements Middleware {
    public async handle(request: Request, response: Response, ...roles: string[]) {
        // roles = ["admin", "superadmin"]
    }
}

You can pass complex or typed parameters (numbers, objects, enums, callbacks) directly through the constructor:

typescript
export class ThrottleMiddleware implements Middleware {
    constructor(private maxAttempts = 60, private decayMinutes = 1) {}

    public async handle(request: Request, response: Response): Promise<void> {
        // use this.maxAttempts and this.decayMinutes
    }
}

Then pass the instantiated object directly to .middleware():

typescript
Route.get("/api/sensitive", [DataController, "show"])
    .middleware(new ThrottleMiddleware(10, 5));

6. Fluent Factory Functions ​

You can also export fluent helper functions to keep routes clean and expressive:

typescript
export const throttle = (max: number, decay: number) => new ThrottleMiddleware(max, decay);

Route.get("/api/sensitive", [DataController, "show"])
    .middleware(throttle(10, 5));

Selective Middleware Exclusion (.withoutMiddleware) ​

Remove auto-loaded middlewares (such as VerifyCsrfToken or StartSession) for specific endpoints:

typescript
import { VerifyCsrfToken, StartSession } from "struxjs";

// Exclude CSRF protection by Class Reference
Route.post("/payment/webhook", [WebhookController, "handle"])
    .withoutMiddleware(VerifyCsrfToken);

// Exclude by string alias
Route.post("/api/callback", "CallbackController@handle")
    .withoutMiddleware("csrf");

// Exclude multiple middlewares
Route.get("/stateless", "ApiController@show")
    .withoutMiddleware([VerifyCsrfToken, StartSession]);

Attaching Middleware to Route Groups ​

Apply middleware to a group of routes using .middleware() or .use() on Route:

typescript
import { Route } from "struxjs";
import { AuthMiddleware } from "../app/Middleware/AuthMiddleware.js";

// 1. Fluent .middleware().group()
Route.middleware([AuthMiddleware]).group(() => {
    Route.get("/dashboard", [UserController, "dashboard"]);
    Route.get("/settings", [UserController, "settings"]);
});

// 2. Fluent .use().group()
Route.use(AuthMiddleware).group(() => {
    Route.get("/reports", [ReportController, "index"]);
});

// 3. Options Object Grouping
Route.group({ middleware: [AuthMiddleware], prefix: "/admin" }, () => {
    Route.get("/users", [AdminController, "users"]);
});

Registering Global Middleware ​

Global middlewares execute automatically on every incoming HTTP request. Register global middlewares using static Route.use() before defining application routes:

typescript
import { Route } from "struxjs";
import { AuthMiddleware } from "../app/Middleware/AuthMiddleware.js";
import { SecurityHeadersMiddleware } from "../app/Middleware/SecurityHeadersMiddleware.js";

// Global Middlewares applied to ALL HTTP requests
Route.use(SecurityHeadersMiddleware);
Route.use(AuthMiddleware);

Dependency Injection in Middleware ​

Since Middleware classes are instantiated by the StruxJS IoC Container, dependencies can be injected automatically.

1. Constructor Injection ​

A. Direct Type Hinting ​

Type-hint services in the Middleware constructor:

typescript
import { Middleware, Request, Response } from "struxjs";
import { AuthService } from "../Services/AuthService.js";

export class AuthMiddleware implements Middleware {
    constructor(private authService: AuthService) {}

    public async handle(request: Request, response: Response) {
        const token = request.header("authorization");
        const isValid = await this.authService.validateToken(token);

        if (!isValid) {
            return response.status(401).json({ error: "Invalid token" });
        }
    }
}

B. @Inject() Decorator in Constructor ​

Explicitly inject string tokens or Class types into constructor parameters using @Inject():

typescript
import { Inject, Middleware, Request, Response } from "struxjs";
import { AuthService } from "../Services/AuthService.js";

export class AuthMiddleware implements Middleware {
    constructor(
        @Inject("auth.service") private authService: AuthService
    ) {}

    public async handle(request: Request, response: Response) {
        // ...
    }
}

2. Manual Resolution (make()) ​

Resolve services manually using make():

typescript
import { make, Middleware, Request, Response } from "struxjs";
import { AuthService } from "../Services/AuthService.js";

export class AuthMiddleware implements Middleware {
    public async handle(request: Request, response: Response) {
        const authService = make(AuthService);
        // ...
    }
}

Built-in Framework Middlewares ​

StruxJS includes several ready-to-use middleware components for authentication, authorization, and security. All parameterized middlewares support both Instance Constructors (with type-safe options) and String Aliases (colon syntax):

Middleware ClassTypical AliasParametersInstance UsageString Alias Usage
AuthMiddleware"auth"redirectTo?, guard?new AuthMiddleware("/admin/login", "admin") or AuthMiddleware.redirectTo("/admin/login")"auth:/admin/login,admin"
ApiAuthMiddleware"apiauth"guard?new ApiAuthMiddleware("admin") or ApiAuthMiddleware.guard("admin")"apiauth:admin"
CanMiddleware"can"abilitynew CanMiddleware("edit-post") or can("edit-post")"can:edit-post"
RoleMiddleware"role"...rolesnew RoleMiddleware("admin", "editor") or role("admin", "editor")"role:admin,editor"
PermissionMiddleware"permission"...permissionsnew PermissionMiddleware("publish-post") or permission("publish-post")"permission:publish-post"
VerifyCsrfToken"csrf"(none)VerifyCsrfToken"csrf"
RouteCacheMiddleware"cache"durationSeconds?new RouteCacheMiddleware(300)"cache:300"

For comprehensive guides on these security middlewares, see:

Chaining Example: Authenticated API with RBAC ​

typescript
import { Route, can, role, permission } from "struxjs";
import { ApiAuthMiddleware } from "../app/Middleware/ApiAuthMiddleware.js";

// Option A: Class reference or static factory
Route.middleware([ApiAuthMiddleware, role("admin")]).group(() => {
    Route.get("/api/admin/users", [AdminController, "users"]);
    Route.get("/api/admin/metrics", [AdminController, "metrics"]);
});

// Option B: String aliases
Route.middleware(["apiauth", "permission:delete-users"])
    .delete("/api/users/:id", [UserController, "destroy"]);

// Option C: Instance constructor
Route.middleware(["apiauth", can("update-post")])
    .put("/api/posts/:id", [PostController, "update"]);

Released under the MIT License.