Better Auth

createAuthMiddleware

Framework-agnostic factory with route filtering, session timing, and lifecycle hooks. Call it once at startup and reuse across requests.

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.

server/middleware/auth-identify.ts
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:

OptionTypeDefaultDescription
excludestring[]['/api/auth/**']Route patterns to skip (glob).
includestring[]undefinedIf set, only matching routes are resolved.
onIdentify(log, session) => voidundefinedCalled after successful identification.
onAnonymous(log) => voidundefinedCalled when no session is found.

Skip the routes that do not need a session

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 and 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, and exclude wins over include.

React to identification

Use onIdentify to react to user identification, for example to force-keep logs for premium users via tail sampling:

server/middleware/auth-identify.ts
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.

Common patterns for 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.

What happens when auth fails

The middleware catches every error from getSession and logs nothing, so 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.