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:
- Global Middleware — Applied to all requests
- Middleware Groups — Web or API group middleware
- Route Middleware — Specific middleware assigned to the route
- Controller/Handler — Your route handler executes
- 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.