Middleware
Middleware allows you to filter and modify HTTP requests before they reach your controllers.
Quick Start
Lockness provides a powerful decorator-based middleware system:
// 1. Declare a middleware with a name
@DeclareMiddleware('auth')
export class AuthMiddleware implements IMiddleware {
async handle(c: Context, next: Next) {
// Your middleware logic
await next()
}
}
// 2. Use it in controllers
@Controller('/dashboard')
export class DashboardController {
@Get('/')
@UseMiddleware('auth')
index(c: Context) {
return c.json({ dashboard: true })
}
}
Built-in Middleware
Lockness provides access to all Hono middleware directly from @lockness/core.
No need to install or import from separate packages!
Common Middleware Examples
Security & CORS:
import { cors, csrf, secureHeaders } from '@lockness/core'
app.useMiddleware(
cors({ origin: 'https://example.com' }),
csrf(),
secureHeaders(),
)
Request Handling:
import { bodyLimit, logger, requestId } from '@lockness/core'
app.useMiddleware(
logger(),
requestId(),
bodyLimit({ maxSize: 50 * 1024 }), // 50KB limit
)
Performance:
import { cache, compress, etag } from '@lockness/core'
app.useMiddleware(
compress(),
etag(),
cache({ cacheName: 'my-app', cacheControl: 'max-age=3600' }),
)
Authentication:
import { basicAuth, bearerAuth, jwt } from '@lockness/core'
// HTTP Basic Auth
app.useMiddleware(basicAuth({ username: 'admin', password: 'secret' }))
// Bearer Token
app.useMiddleware(bearerAuth({ token: 'secret-token' }))
// JWT
app.useMiddleware(jwt({ secret: 'jwt-secret' }))
Complete Example:
import {
App,
compress,
cors,
csrf,
logger,
requestId,
secureHeaders,
} from '@lockness/core'
const app = new App()
app.useMiddleware(
logger(),
requestId(),
cors(),
csrf(),
secureHeaders(),
compress(),
)
await app.init({ controllersDir: './app/controller' })
For a complete list of available middleware, see:
- Hono Middleware Documentation
- All middleware listed there are available from
@lockness/core
Creating Middleware
Generate a new middleware class:
deno task cli make:middleware Auth
This creates app/middleware/auth_middleware.ts:
import {
type Context,
DeclareMiddleware,
type IMiddleware,
type Next,
} from '@lockness/core'
/**
* Auth middleware - checks if user is authenticated
*
* This middleware is automatically registered as 'auth' via @DeclareMiddleware.
* Use it in controllers with @UseMiddleware('auth')
*/
@DeclareMiddleware('auth')
export class AuthMiddleware implements IMiddleware {
async handle(c: Context, next: Next) {
const authHeader = c.req.header('Authorization')
if (!authHeader) {
return c.json({ error: 'Unauthorized' }, 401)
}
// Verify token, get user, attach to context
// c.set('user', user)
await next()
}
}
The @DeclareMiddleware Decorator
The @DeclareMiddleware(name) decorator automatically registers your middleware
in a global registry. When you call app.init() with middlewaresDir, all
middlewares in that directory are auto-discovered and registered.
// Middleware is registered as 'admin' - no manual registration needed!
@DeclareMiddleware('admin')
export class AdminMiddleware implements IMiddleware {
async handle(c: Context, next: Next) {
const user = c.get('user')
if (!user?.isAdmin) {
return c.json({ error: 'Admin access required' }, 403)
}
await next()
}
}
Auto-Discovery
Configure middleware auto-discovery in your kernel:
// app/kernel.tsx
await app.init({
// Auto-discover middlewares decorated with @DeclareMiddleware
middlewaresDir: './app/middleware',
// Controllers
controllersDir: './app/controller',
})
All files in middlewaresDir are imported, and classes decorated with
@DeclareMiddleware are automatically registered. No need to manually import or
configure each middleware!
Global Middleware
Apply middleware to all routes in app/kernel.ts:
import { LoggerMiddleware } from '@middleware/logger_middleware.ts'
// Configure global middlewares using fluent API
app.useMiddleware(
sessionMiddleware(),
LoggerMiddleware,
)
await app.init({
middlewaresDir: './app/middleware',
controllersDir: './app/controller',
})
Named Middleware (Legacy)
You can still manually register middlewares for backward compatibility:
import { AuthMiddleware } from '@middleware/auth_middleware.ts'
import { AdminMiddleware } from '@middleware/admin_middleware.ts'
await app.init({
controllers,
middlewares: {
auth: AuthMiddleware,
admin: AdminMiddleware,
},
})
Using Middleware in Controllers
With @UseMiddleware (Recommended)
Use the @UseMiddleware decorator to apply middleware to route methods:
import { Controller, Get, UseMiddleware } from '@lockness/core'
@Controller('/dashboard')
export class DashboardController {
@Get('/')
@UseMiddleware('auth')
index(c: Context) {
return c.json({ dashboard: true })
}
}
Stacking Multiple Middlewares
Apply multiple middlewares - they execute top-to-bottom:
@Controller('/admin')
export class AdminController {
@Get('/users')
@UseMiddleware('auth') // Runs first
@UseMiddleware('admin') // Runs second
users(c: Context) {
return c.json({ users: [] })
}
}
With Class Reference
You can also use middleware classes directly:
import { AuthMiddleware } from '@middleware/auth_middleware.ts'
@Controller('/api')
export class ApiController {
@Get('/data')
@UseMiddleware(AuthMiddleware)
getData(c: Context) {
return c.json({ data: [] })
}
}
Middleware Examples
Logger Middleware:
@DeclareMiddleware('logger')
export class LoggerMiddleware implements IMiddleware {
async handle(c: Context, next: Next) {
const start = Date.now()
await next()
const ms = Date.now() - start
console.log(`${c.req.method} ${c.req.url} - ${ms}ms`)
}
}
CORS Middleware:
@DeclareMiddleware('cors')
export class CorsMiddleware implements IMiddleware {
async handle(c: Context, next: Next) {
c.header('Access-Control-Allow-Origin', '*')
c.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
await next()
}
}
Rate Limiting:
const requests = new Map<string, number>()
@DeclareMiddleware('rate-limit')
export class RateLimitMiddleware implements IMiddleware {
async handle(c: Context, next: Next) {
const ip = c.req.header('x-forwarded-for') || 'unknown'
const count = requests.get(ip) || 0
if (count > 100) {
return c.json({ error: 'Rate limit exceeded' }, 429)
}
requests.set(ip, count + 1)
await next()
}
}
Middleware Order
Middleware execution order:
- Global middlewares - Applied to all routes (via
app.useMiddleware()) - Route-level middlewares - Applied via
@UseMiddleware(top-to-bottom)
Example execution order:
// Global middlewares via fluent API
app.useMiddleware(LoggerMiddleware)
@Controller('/api')
export class ApiController {
@Get('/users')
@UseMiddleware('auth') // Runs after global middlewares
@UseMiddleware('cache') // Runs after auth
users(c: Context) { ... }
}
// Execution order: Logger → Auth → Cache → users()
Migration from @Use to @UseMiddleware
The @Use decorator is deprecated in favor of @UseMiddleware for clearer
intent.
Before (deprecated):
import { Use } from '@lockness/core'
@Get('/protected')
@Use('auth')
@Use(AuthMiddleware)
protected(c: Context) { ... }
After (recommended):
import { UseMiddleware } from '@lockness/core'
@Get('/protected')
@UseMiddleware('auth')
@UseMiddleware(AuthMiddleware)
protected(c: Context) { ... }
Both decorators work identically, but @UseMiddleware is preferred for new
code.
Composing Middlewares
Lockness provides a compose() helper to combine multiple middlewares into a
single reusable unit. This is useful for creating middleware stacks that you can
apply in multiple places.
Basic Usage
import { compose, cors, logger } from '@lockness/core'
// Combine multiple middlewares into one
const apiStack = compose([
logger(), // Hono function middleware
AuthMiddleware, // Lockness class middleware
'admin', // Named middleware (resolved from registry)
])
@Controller('/api')
export class ApiController {
@Get('/users')
@UseMiddleware(apiStack)
users(c: Context) {
return c.json({ users: [] })
}
}
Alternative Syntax
Use composeMiddleware() with rest parameters instead of an array:
import { composeMiddleware, cors, logger } from '@lockness/core'
const stack = composeMiddleware(
logger(),
AuthMiddleware,
'admin',
)
Nested Composition
You can compose composed middlewares for complex stacks:
// Base auth stack
const authStack = compose([
sessionMiddleware(),
'auth',
])
// Admin stack builds on auth
const adminStack = compose([
authStack, // Reuse the auth stack
'admin',
AuditMiddleware,
])
// Use the complete stack
@Controller('/admin')
@UseMiddleware(adminStack)
export class AdminController { ... }
Supported Middleware Types
The compose() function accepts:
- Hono middleware functions:
cors(),logger(),sessionMiddleware() - Lockness class middlewares: Classes implementing
IMiddleware - Named middlewares: String names registered via
@DeclareMiddleware - Composed middlewares: Output of another
compose()call
Short-Circuiting
If any middleware in the composed stack returns a Response, the chain stops
and that response is returned immediately:
const protectedStack = compose([
async (c, next) => {
if (!c.get('user')) {
return c.json({ error: 'Unauthorized' }, 401)
}
await next()
},
AdminMiddleware,
])