Honovel Honovel Deno

Middleware in Honovel

Middleware provides a convenient mechanism for filtering and inspecting HTTP requests entering your application. Think of middleware as layers that requests must pass through before reaching your application's routes and controllers.

How Middleware Works

Each middleware can perform tasks before or after passing the request to the next layer:

  • Authenticate users before they access protected routes
  • Log requests for debugging and analytics
  • Validate CSRF tokens to prevent cross-site attacks
  • Transform request data (trim strings, parse JSON, etc.)
  • Add headers to responses
  • Handle CORS policies

Creating Middleware

Generate a new middleware using the CLI:

deno task smelt make:middleware CheckAge

This creates a file at app/Http/Middlewares/CheckAge.ts:

export default class CheckAge {
    async handle({ request }, next) {
        // Perform action before the request
        const age = request.query('age');
        
        if (!age || parseInt(age) < 18) {
            return response().json({ error: 'You must be 18 or older' }, 403);
        }

        await next(); // Pass to next middleware/controller

        // Perform action after the request (optional)
    }
}

Registering Middleware

Middleware is registered in app/Http/Kernel.ts. There are three types:

1. Global Middleware

Runs on every request to your application:

protected override middleware = [
    TrustProxies,
    HandleCors,
    PayloadParser,
    ValidatePostSize,
    TrimStrings,
    ConvertEmptyStringsToNull,
];

2. Middleware Groups

Applied to specific route groups (web or api):

protected override middlewareGroups = {
    web: [
        EncryptCookies,
        StartSession,
        VerifyCsrfToken,
        SubstituteBindings,
    ],
    api: [
        SubstituteBindings,
    ],
};

3. Route Middleware

Named middleware applied to specific routes:

protected override routeMiddleware = {
    auth: Authenticate,
    'auth.basic': AuthenticateWithBasicAuth,
    'cache.headers': SetCacheHeaders,
    can: Authorize,
    guest: RedirectIfAuthenticated,
    throttle: ThrottleRequests,
    verified: EnsureEmailIsVerified,
    bind_content: BindContent,
};

Applying Middleware to Routes

Use the .middleware() method to apply middleware to routes:

// Single middleware
Route.get("/admin", [AdminController, "index"]).middleware("auth");

// Multiple middleware
Route.get("/profile", [ProfileController, "show"])
    .middleware("auth")
    .middleware("verified");

// Middleware with parameters
Route.get("/api/data", [ApiController, "data"])
    .middleware("throttle:60,1"); // 60 requests per minute

// Apply to route groups
Route.group({ middleware: ["auth", "verified"] }, () => {
    Route.get("/dashboard", [DashboardController, "index"]);
    Route.get("/settings", [SettingsController, "index"]);
});

Built-in Middleware

Honovel includes several middleware out of the box:

Middleware Key Description
Authenticate auth Ensures user is authenticated
RedirectIfAuthenticated guest Redirect authenticated users away from guest routes
VerifyCsrfToken - Protects against CSRF attacks (web group)
ThrottleRequests throttle Rate limiting for routes
SubstituteBindings - Resolves route parameters to models
EnsureEmailIsVerified verified Ensures user's email is verified
ValidateSignature signed Validates signed URLs
HandleCors - Handles CORS headers (global)
TrimStrings - Trims whitespace from request inputs

Middleware Parameters

Some middleware accept parameters separated by colons:

// Throttle: 100 requests per minute
Route.get("/api/posts", [PostController, "index"])
    .middleware("throttle:100,1");

// Cache headers: public, max-age 3600 seconds
Route.get("/api/public", [ApiController, "public"])
    .middleware("cache.headers:public;max_age=3600");

Middleware Execution Order

Middleware executes in the following order:

  1. Global Middleware — Applied to all requests
  2. Middleware Groups — Web or API group middleware
  3. Route Middleware — Specific middleware assigned to the route
  4. Controller/Handler — Your route handler executes
  5. Middleware (reverse) — Post-request middleware logic runs

Advanced Example: Logging Middleware

import { IMiddleware } from "Illuminate/Contracts/IMiddleware.ts";

export default class LogRequests implements IMiddleware {
    async handle({ request }, next) {
        const start = Date.now();
        const method = request.method;
        const path = request.path;

        console.log(`[${method}] ${path} - Request started`);

        await next(); // Process the request

        const duration = Date.now() - start;
        console.log(`[${method}] ${path} - Completed in ${duration}ms`);
    }
}

Custom Model Binding

The SubstituteBindings middleware resolves route parameters to models. Register your bindings in vendor/honovel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.ts:

private readonly routerBindings: Record<string, typeof Model<ModelWithAttributes>> = {
    user: User,      // {user} → User model
    post: Post,      // {post} → Post model
    comment: Comment // {comment} → Comment model
};

Now in your routes:

Route.get("/users/{user}", [UserController, "show"]);
// The {user} parameter will be automatically resolved to a User model instance

Best Practices

  • Keep middleware focused on a single responsibility
  • Use global middleware sparingly to avoid performance impact
  • Apply route middleware only where needed
  • Remember: middleware order matters!
  • Always call await next() to pass control forward

Summary

Middleware is a powerful tool for handling cross-cutting concerns in your application. By understanding how to create, register, and apply middleware, you can build more secure, maintainable, and organized Honovel applications.