Documentation Index Fetch the complete documentation index at: https://mintlify.com/adonisjs/core/llms.txt
Use this file to discover all available pages before exploring further.
Middleware are functions that execute before your route handlers. They can transform the request, validate authentication, log requests, or terminate the request early.
Creating middleware
Use the make:middleware Ace command to create a new middleware:
node ace make:middleware Auth
You’ll be prompted to select a middleware stack:
server : Runs for all HTTP requests
router : Runs for all matched routes
named : Applied selectively to specific routes
This creates a middleware file at app/middleware/auth_middleware.ts:
import type { HttpContext } from '@adonisjs/core/http'
import type { NextFn } from '@adonisjs/core/types/http'
export default class AuthMiddleware {
async handle ( ctx : HttpContext , next : NextFn ) {
/**
* Middleware logic goes here (before the next call)
*/
console . log ( ctx )
/**
* Call next method in the pipeline and return its output
*/
const output = await next ()
return output
}
}
Middleware execution flow
Middleware execute in a pipeline:
export default class LoggerMiddleware {
async handle ( ctx : HttpContext , next : NextFn ) {
// Code here runs BEFORE the route handler
console . log ( 'Request received:' , ctx . request . url ())
// Call the next middleware/handler
const output = await next ()
// Code here runs AFTER the route handler
console . log ( 'Response sent:' , ctx . response . getStatus ())
return output
}
}
Middleware stacks
Server middleware
Server middleware runs for every HTTP request , even if the route doesn’t exist. Configure in start/kernel.ts:
import server from '@adonisjs/core/services/server'
server . use ([
() => import ( '#middleware/container_bindings_middleware' ),
() => import ( '#middleware/logger_middleware' ),
() => import ( '@adonisjs/bodyparser/bodyparser_middleware' ),
])
Use cases:
CORS handling
Request logging
Body parsing
Setting global headers
Router middleware
Router middleware runs only for matched routes . Configure in start/kernel.ts:
import router from '@adonisjs/core/services/router'
router . use ([
() => import ( '#middleware/auth_middleware' ),
])
Use cases:
Authentication checks
CSRF protection
Rate limiting for all routes
Named middleware
Named middleware is applied selectively to specific routes . Register in start/kernel.ts:
import router from '@adonisjs/core/services/router'
export const middleware = router . named ({
auth : () => import ( '#middleware/auth_middleware' ),
admin : () => import ( '#middleware/admin_middleware' ),
throttle : () => import ( '#middleware/throttle_middleware' ),
})
Apply to routes:
import router from '@adonisjs/core/services/router'
import { middleware } from '#start/kernel'
router
. get ( '/dashboard' , '#controllers/dashboard_controller.index' )
. use ( middleware . auth ())
router
. get ( '/admin' , '#controllers/admin_controller.index' )
. use ([ middleware . auth (), middleware . admin ()])
Writing middleware logic
Authentication middleware
import type { HttpContext } from '@adonisjs/core/http'
import type { NextFn } from '@adonisjs/core/types/http'
export default class AuthMiddleware {
async handle ({ auth , response } : HttpContext , next : NextFn ) {
// Check if user is authenticated
try {
await auth . authenticate ()
} catch ( error ) {
return response . unauthorized ({ error: 'Unauthorized' })
}
// Continue to the next middleware/handler
return await next ()
}
}
CORS middleware
import type { HttpContext } from '@adonisjs/core/http'
import type { NextFn } from '@adonisjs/core/types/http'
export default class CorsMiddleware {
async handle ({ request , response } : HttpContext , next : NextFn ) {
// Set CORS headers
response . header ( 'Access-Control-Allow-Origin' , '*' )
response . header ( 'Access-Control-Allow-Methods' , 'GET, POST, PUT, DELETE' )
response . header ( 'Access-Control-Allow-Headers' , 'Content-Type' )
// Handle preflight requests
if ( request . method () === 'OPTIONS' ) {
return response . noContent ()
}
return await next ()
}
}
Request logging middleware
import type { HttpContext } from '@adonisjs/core/http'
import type { NextFn } from '@adonisjs/core/types/http'
export default class LoggerMiddleware {
async handle ({ request , response , logger } : HttpContext , next : NextFn ) {
const start = Date . now ()
// Log incoming request
logger . info ( `→ ${ request . method () } ${ request . url () } ` )
// Continue to next middleware/handler
const output = await next ()
// Log response
const duration = Date . now () - start
logger . info ( `← ${ response . getStatus () } ${ request . url () } ( ${ duration } ms)` )
return output
}
}
Rate limiting middleware
import type { HttpContext } from '@adonisjs/core/http'
import type { NextFn } from '@adonisjs/core/types/http'
export default class ThrottleMiddleware {
async handle ({ request , response } : HttpContext , next : NextFn ) {
const key = request . ip ()
const requests = await redis . incr ( `rate_limit: ${ key } ` )
if ( requests === 1 ) {
await redis . expire ( `rate_limit: ${ key } ` , 60 ) // 60 seconds
}
if ( requests > 100 ) {
return response . tooManyRequests ({
error: 'Too many requests'
})
}
return await next ()
}
}
Terminating requests early
Middleware can terminate the request without calling next():
export default class MaintenanceMiddleware {
async handle ({ response } : HttpContext , next : NextFn ) {
const isMaintenanceMode = true // Check from config or database
if ( isMaintenanceMode ) {
return response . serviceUnavailable ({
message: 'Service is under maintenance'
})
}
return await next ()
}
}
Middleware with parameters
Pass parameters to middleware:
import type { HttpContext } from '@adonisjs/core/http'
import type { NextFn } from '@adonisjs/core/types/http'
export default class RoleMiddleware {
async handle (
{ auth , response } : HttpContext ,
next : NextFn ,
options : { role : string }
) {
const user = await auth . user
if ( user . role !== options . role ) {
return response . forbidden ({ error: 'Access denied' })
}
return await next ()
}
}
Use it in routes:
router
. get ( '/admin' , '#controllers/admin_controller.index' )
. use ( middleware . role ({ role: 'admin' }))
Applying middleware to route groups
Apply middleware to multiple routes using route groups:
import router from '@adonisjs/core/services/router'
import { middleware } from '#start/kernel'
router . group (() => {
router . get ( '/posts' , '#controllers/posts_controller.index' )
router . post ( '/posts' , '#controllers/posts_controller.store' )
router . delete ( '/posts/:id' , '#controllers/posts_controller.destroy' )
}). middleware ([ middleware . auth ()])
Nested middleware
Combine middleware from groups and individual routes:
router . group (() => {
// All routes in this group require authentication
router . get ( '/posts' , '#controllers/posts_controller.index' )
router
. delete ( '/posts/:id' , '#controllers/posts_controller.destroy' )
. use ( middleware . admin ()) // Also requires admin role
}). middleware ([ middleware . auth ()])
Middleware execution order
Middleware executes in this order:
Server middleware (all requests)
Router middleware (matched routes only)
Route group middleware (outer to inner groups)
Route-specific middleware
Route handler
Example:
// start/kernel.ts
server . use ([() => import ( '#middleware/logger_middleware' )])
router . use ([() => import ( '#middleware/cors_middleware' )])
// start/routes.ts
router . group (() => {
router
. get ( '/admin/users' , '#controllers/admin/users_controller.index' )
. use ( middleware . admin ())
}). middleware ([ middleware . auth ()])
// Execution order:
// 1. LoggerMiddleware
// 2. CorsMiddleware
// 3. AuthMiddleware
// 4. AdminMiddleware
// 5. UsersController.index
Inline middleware
Define middleware inline for simple use cases:
router
. get ( '/users' , '#controllers/users_controller.index' )
. use ( async ({ request , response }, next ) => {
if ( ! request . header ( 'api-key' )) {
return response . unauthorized ({ error: 'API key required' })
}
return await next ()
})
Best practices
Unless you’re intentionally terminating the request, always call next() and return its output. Forgetting to call next() will cause requests to hang.
Use appropriate middleware stack
Use server middleware only for logic that applies to ALL requests
Use router middleware for logic that applies to all matched routes
Use named middleware for selective application to specific routes
Each middleware should have a single responsibility. If your middleware is doing multiple things, split it into separate middleware.
Wrap middleware logic in try-catch blocks to handle errors: async handle ( ctx : HttpContext , next : NextFn ) {
try {
await this . performCheck ( ctx )
return await next ()
} catch ( error ) {
return ctx . response . internalServerError ({ error: error . message })
}
}