Answer
Use Nitro server middleware in server/middleware for a single authorization check that runs before every /api/* handler. In Nuxt 3 stable with Nitro, files placed in server/middleware are auto-registered by Nitro and execute for every server request, including all routes generated from server/api. This is the supported place for shared server/api authorization. Nuxt client route middleware does not apply to server API routes, and auto-imported Nuxt composables are not available in the server runtime.
An imported handler wrapper works but requires manual import in each server/api file. Nitro route middleware via server/middleware is the canonical, DRY option. Nuxt server plugins in server/plugins are for Nitro setup, not per-request interception.
Confirmed facts
server/middleware/*.ts files are automatically registered by Nitro and run in the server-only runtime before the matched server/api handler.- Middleware receives an H3
event object. Authorization must read raw headers, cookies or event.node.req because Nuxt composables such as useAuth are unavailable in this context. - Middleware runs on every server request, not only
/api. Conditional path checks are required to skip public endpoints.
Steps for this case
- Create
server/middleware/auth.ts. Implement an H3 handler that inspects the request and returns event.node.res or sendError for unauthorized requests. - Guard the check with a path allowlist so public routes like
/api/auth/login are not blocked. - Keep the logic synchronous where possible and avoid heavy I/O on the hot path, because middleware runs for all requests.
- Verify execution: add a temporary console log in the middleware and call any
/api endpoint. The log should appear before the API handler runs. Then call a protected endpoint without a token and confirm a 401 response is returned by the middleware, not the handler.
// server/middleware/auth.ts
export default defineEventHandler((event) => {
const url = event.node.req.url || ''
if (url.startsWith('/api/auth/')) return
const token = getHeader(event, 'authorization')
if (!token) {
throw createError({ statusCode: 401, statusMessage: 'Unauthorized' })
}
// token validation here
})
Likely explanation and uncertainty
Middleware file ordering is alphabetical in Nitro. If ordering matters, prefix files with a numeric order. The exact stability of minor-release behavior for middleware ordering and H3 event shape is well established in Nuxt 3, but changes to Nitro internals could affect edge cases. Mark for review if you rely on undocumented event properties.
One diagnostic detail that changes the recommendation: do you need per-route opt-out or different auth schemes per route group? If yes, a path-based allowlist in the same middleware is sufficient; if you need route-specific schemes, a wrapper factory imported by server/api handlers may be clearer.