Guide/guides

Middleware Guide

Guren routes and applications share Hono's middleware model but expose Laravel-style ergonomics for common tasks. You can register middleware globally on the Application instance, per-route via the routing DSL, or through named aliases and groups.

Middleware Guide

Guren routes and applications share Hono's middleware model but expose Laravel-style ergonomics for common tasks. You can register middleware globally on the Application instance, per-route via the routing DSL, or through named aliases and groups.

Global Middleware

// src/app.ts
import { createApp, defineMiddleware } from '@guren/core'

const requestTimer = defineMiddleware(async (ctx, next) => {
  const started = performance.now()
  await next()
  const duration = Math.round(performance.now() - started)
  console.log(`${ctx.req.method} ${ctx.req.path} -> ${ctx.res.status} (${duration}ms)`)
})

const app = createApp()
app.use('*', requestTimer)

Global middlewares run before any routes are mounted. Providers can register middleware inside their boot() hook using the application instance.

Route Middleware

import { Router } from '@guren/core'
import DashboardController from '@/app/Http/Controllers/DashboardController'
import { requireAuthenticated } from '@/app/Http/middleware/auth'

export function registerWebRoutes(router: Router): void {
  router.get('/dashboard', [DashboardController, 'index']).middleware(
    requireAuthenticated({ redirectTo: '/login' }),
  )
}

Route middleware only applies to the specific endpoint (or every endpoint nested in a group).

Middleware Aliases

Register short string names for middleware functions so you can reference them throughout your route files without importing the actual function each time:

import { Router, requireAuthenticated } from '@guren/core'
import { requireAdmin } from '@/app/Http/middleware/admin'
import { csrfProtection } from '@/app/Http/middleware/csrf'

export function registerWebRoutes(router: Router): void {
  router.aliasMiddleware('auth', requireAuthenticated())
  router.aliasMiddleware('admin', requireAdmin())
  router.aliasMiddleware('csrf', csrfProtection())
}

Once registered, use the alias string anywhere middleware is accepted:

router.get('/dashboard', [DashboardController, 'index']).middleware('auth')
router.post('/settings', [SettingsController, 'update']).middleware('auth', 'csrf')

Middleware Groups

Bundle multiple middleware aliases under a single group name. This is useful for stacks that commonly run together:

router.groupMiddleware('web', ['session', 'csrf'])
router.groupMiddleware('api', ['throttle:60'])

Apply a group to a route group using router.middleware():

router.middleware('web').group((web) => {
  web.get('/', [HomeController, 'index'])
  web.get('/about', [PagesController, 'about'])
  web.get('/contact', [PagesController, 'contact'])
})

router.middleware('api').group((api) => {
  api.get('/api/posts', [ApiPostController, 'index'])
  api.post('/api/posts', [ApiPostController, 'store'])
})

You can combine groups and individual aliases:

router.middleware('web', 'auth').group((group) => {
  group.get('/profile', [ProfileController, 'show'])
  group.put('/profile', [ProfileController, 'update'])
})

Built-in Helpers

defineMiddleware

Utility wrapper for annotating Hono middleware with Guren's type expectations.

createSessionMiddleware

Factory that attaches a session object to the request context. Sessions are stored in memory by default (MemorySessionStore) and persisted using signed cookies.

import { createSessionMiddleware } from '@guren/core'

app.use('*', createSessionMiddleware())

Each request exposes the session through ctx.get('guren:session') or the helper getSessionFromContext(ctx).

Auth Guards

requireAuthenticated and requireGuest are thin wrappers that expect an auth context to be attached earlier in the pipeline. Pair them with attachAuthContext, which stores your guard implementation on the request.

import { attachAuthContext, requireAuthenticated } from '@guren/core'

app.use('*', attachAuthContext(() => authManager.createGuard('web')))

// Using middleware alias (recommended)
router.aliasMiddleware('auth', requireAuthenticated({ redirectTo: '/login' }))
router.aliasMiddleware('guest', requireGuest({ redirectTo: '/dashboard' }))

router.middleware('auth').group((auth) => {
  auth.get('/settings', [SettingsController, 'index'])
  auth.get('/dashboard', [DashboardController, 'index'])
})

CSRF Protection

The CSRF middleware validates tokens on state-changing requests (POST, PUT, PATCH, DELETE):

router.aliasMiddleware('csrf', csrfProtection())

router.middleware('csrf').group((csrf) => {
  csrf.post('/posts', [PostsController, 'store'])
})

Rate Limiting

Apply rate limiting to routes or groups:

import { rateLimit } from '@guren/core'

router.aliasMiddleware('throttle', rateLimit({ max: 60, windowMs: 60_000 }))

router.middleware('throttle').group((throttle) => {
  throttle.post('/api/login', [AuthController, 'login'])
})

Writing Custom Middleware

Create middleware with defineMiddleware for full type support:

import { defineMiddleware } from '@guren/core'

export const requireSubscription = defineMiddleware(async (ctx, next) => {
  const user = await ctx.get('auth')?.user()

  if (!user?.isSubscribed) {
    return ctx.json({ error: 'Subscription required' }, 403)
  }

  await next()
})

Register it as an alias for convenient use:

router.aliasMiddleware('subscribed', requireSubscription)

router.middleware('auth', 'subscribed').group((group) => {
  group.get('/premium', [PremiumController, 'index'])
})