Middleware
Protect routes with middleware at different levels
Middleware
Girouette provides flexible middleware application at three levels: individual routes, resource actions, and entire controller groups.
Route-Level Middleware
Apply middleware to individual routes using the @Middleware decorator:
import { Get, Post, Middleware } from '@adonisjs-community/girouette'
import { middleware } from '#start/kernel'
import { HttpContext } from '@adonisjs/core/http'
export default class ProfileController {
@Get('/profile')
@Middleware([middleware.auth()])
async show({ auth }: HttpContext) {
return auth.user
}
@Post('/profile')
@Middleware([middleware.auth(), middleware.verified()])
async update({ auth, request }: HttpContext) {
return auth.user!.merge(request.body()).save()
}
@Get('/public-profile/:username')
async publicProfile({ params }: HttpContext) {
// No middleware - publicly accessible
return User.findByOrFail('username', params.username)
}
}Multiple Middleware
Stack multiple middleware by passing an array:
@Post('/admin/users')
@Middleware([
middleware.auth(),
middleware.role(['admin']),
middleware.throttle({ maxAttempts: 10 }),
])
async createUser({ request }: HttpContext) {
return User.create(request.body())
}Middleware executes in the order specified in the array.
Group-Level Middleware
Apply middleware to all routes in a controller using @GroupMiddleware:
import { Group, GroupMiddleware, Get, Post, Delete } from '@adonisjs-community/girouette'
import { middleware } from '#start/kernel'
@Group({ prefix: '/admin' })
@GroupMiddleware([middleware.auth(), middleware.role(['admin'])])
export default class AdminController {
@Get('/dashboard')
async dashboard() {
// Protected by auth + admin role
}
@Get('/users')
async listUsers() {
// Protected by auth + admin role
}
@Delete('/users/:id')
async deleteUser() {
// Protected by auth + admin role
}
}Combining Group and Route Middleware
Route-level middleware is applied in addition to group middleware:
@Group({ prefix: '/api' })
@GroupMiddleware([middleware.auth()])
export default class ApiController {
@Get('/profile')
async profile() {
// Only auth middleware
}
@Delete('/account')
@Middleware([middleware.confirmed()])
async deleteAccount() {
// auth middleware (from group) + confirmed middleware
}
}Resource Middleware
For resource controllers, use @ResourceMiddleware to apply middleware to specific actions:
import { Resource, ResourceMiddleware } from '@adonisjs-commXCunity/girouette'
import { middleware } from '#start/kernel'
@Resource('posts')
@ResourceMiddleware(['store', 'update', 'destroy'], [middleware.auth()])
export default class PostsController {
async index() {
// Public - no middleware
return Post.all()
}
async show({ params }: HttpContext) {
// Public - no middleware
return Post.findOrFail(params.id)
}
async store({ request, auth }: HttpContext) {
// Protected by auth middleware
return auth.user!.related('posts').create(request.body())
}
async update({ params, request, auth }: HttpContext) {
// Protected by auth middleware
const post = await Post.findOrFail(params.id)
return post.merge(request.body()).save()
}
async destroy({ params }: HttpContext) {
// Protected by auth middleware
const post = await Post.findOrFail(params.id)
await post.delete()
return { deleted: true }
}
}Multiple Resource Middleware
Apply different middleware to different action groups:
@Resource('articles')
@ResourceMiddleware(['index', 'show'], [middleware.cache()])
@ResourceMiddleware(['store'], [middleware.auth(), middleware.throttle()])
@ResourceMiddleware(['update', 'destroy'], [middleware.auth(), middleware.author()])
export default class ArticlesController {
async index() {
// Cached response
}
async show() {
// Cached response
}
async store() {
// Auth + rate limiting
}
async update() {
// Auth + ownership verification
}
async destroy() {
// Auth + ownership verification
}
}Using String for Single Action
For a single action, you can pass a string instead of an array:
@Resource('comments')
@ResourceMiddleware('destroy', [middleware.auth(), middleware.moderator()])
export default class CommentsController {
// Only destroy is protected
}Middleware Execution Order
Middleware executes in this order:
- Group middleware (from
@GroupMiddleware) - Resource middleware (from
@ResourceMiddleware) - Route middleware (from
@Middleware)
@Group({ prefix: '/api' })
@GroupMiddleware([middleware.logger()]) // 1st
@Resource('posts')
@ResourceMiddleware('store', [middleware.auth()]) // 2nd
export default class PostsController {
@Post('/posts')
@Middleware([middleware.validated()]) // 3rd
async store() {
// Execution: logger → auth → validated
}
}Common Middleware Patterns
Authentication Gate
@GroupMiddleware([middleware.auth()])
export default class ProtectedController {
@Get('/dashboard')
async dashboard() { /* ... */ }
@Get('/settings')
async settings() { /* ... */ }
}API Rate Limiting
@Group({ prefix: '/api' })
@GroupMiddleware([middleware.throttle({ maxAttempts: 60, decay: 60 })])
export default class ApiController {
@Post('/expensive-operation')
@Middleware([middleware.throttle({ maxAttempts: 5, decay: 60 })])
async expensiveOperation() {
// Stricter limit for this endpoint
}
}Guest-Only Routes
export default class AuthController {
@Get('/login')
@Middleware([middleware.guest()])
async showLogin() {
// Only accessible if NOT authenticated
}
@Post('/login')
@Middleware([middleware.guest()])
async login() { /* ... */ }
}Next Steps
- Resources - RESTful resource controllers
- Route Constraints - Validate route parameters