createAuthMiddleware
createAuthMiddleware wraps identifyUser with the things you need on every request: route filtering, session resolution timing, lifecycle hooks, and silent error handling. Call it once at startup, then use the returned function in your framework's middleware/hook system.
import { createAuthMiddleware } from 'evlog/better-auth'
const identify = createAuthMiddleware(auth, {
exclude: ['/api/auth/**', '/api/public/**'],
include: ['/api/**'],
maskEmail: true,
})
The function signature is (log, headers, path?) => Promise<boolean>. It resolves the session, calls identifyUser, captures timing into auth.resolvedIn, fires lifecycle hooks, and silently catches errors so session resolution never breaks a request.
Options
Inherits all identifyUser options, plus:
| Option | Type | Default | Description |
|---|---|---|---|
exclude | string[] | ['/api/auth/**'] | Route patterns to skip (glob). |
include | string[] | undefined | If set, only matching routes are resolved. |
onIdentify | (log, session) => void | undefined | Called after successful identification. |
onAnonymous | (log) => void | undefined | Called when no session is found. |
Route Filtering
Skip Better Auth's own routes and any public endpoints to avoid wasted database queries:
const identify = createAuthMiddleware(auth, {
exclude: [
'/api/auth/**', // Better Auth itself
'/api/public/**', // Public endpoints
'/api/health', // Health checks
],
})
For high-traffic apps, flip the model — only resolve sessions on routes that need them:
const identify = createAuthMiddleware(auth, {
include: ['/api/dashboard/**', '/api/account/**'],
})
include and exclude use glob patterns (*, **). Provide both if you need granular control — exclude wins over include.
Lifecycle Hooks
Use onIdentify to react to user identification — for example, force-keep logs for premium users via tail sampling:
const identify = createAuthMiddleware(auth, {
onIdentify: (log, session) => {
if (session.user.plan === 'enterprise') {
log.set({ _forceKeep: true })
}
},
onAnonymous: (log) => {
log.set({ anonymous: true })
},
})
Hooks fire after the session is resolved and identifyUser has set its fields. They run on every request that passes the include/exclude filter, so keep them fast and side-effect-free.
onIdentify:- Force-keep audit logs for admins or high-value plans.
- Tag the request with feature flags or tenant info loaded from the session.
- Increment a per-user counter for billing.
Error Handling
The middleware catches every error from getSession and logs nothing — your request keeps flowing whether the auth backend is up or down. The wide event still includes auth.resolvedIn and auth.identified: false so you can alert on session resolution health from your dashboards.