@alikhalilll/nuxt-auto-middleware
v1.2.0
Published
Declarative, layout-based route middleware for Nuxt 3/4, with glob matching, named groups, and per-page overrides.
Maintainers
Readme
@alikhalilll/nuxt-auto-middleware
Declarative, layout-based route middleware for Nuxt 3 / 4. Map layouts to middlewares once in your nuxt.config.ts — the module generates a single global route middleware that dispatches the right chain on each navigation.
- Layout → middleware mapping with glob patterns (
dashboard/*,admin*) or RegExp objects. - Named middleware groups for DRY configuration:
{ auth: ['auth', 'verify-role'] }referenced as@auth. - Per-page overrides via
definePageMeta({ middlewares: [...] })orskipAutoMiddleware: true. - Typed registry — the module generates a
AutoMiddlewareNameunion type and augmentsPageMeta, so typos are compile errors. - Early termination — a middleware returning
falsestops the chain; any other value is treated as a navigation result. - Zero runtime deps — just Nuxt.
Table of contents
- Install
- Register the module
- Usage
- Middleware return values
- Typed registry
- Matching rules
- Module options reference
- Exported types
Install
pnpm add @alikhalilll/nuxt-auto-middlewareRegister the module
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@alikhalilll/nuxt-auto-middleware'],
autoMiddleware: {
groups: {
auth: ['auth', 'verify-role'],
},
rules: [
{ layouts: ['default'], middlewares: ['log-activity'] },
{ layouts: ['dashboard', 'dashboard/*'], middlewares: ['@auth'] },
{ layouts: ['admin'], middlewares: ['@auth', 'require-admin'] },
],
debug: false,
pageMetaField: 'middlewares', // set to false to disable per-page extras
},
});Middleware files live in the standard Nuxt location: ~/middleware/auth.ts, ~/middleware/verify-role.ts, etc.
Usage
Basic mapping
One layout, one middleware — the simplest rule:
autoMiddleware: {
rules: [
{ layouts: ['default'], middlewares: ['track-pageview'] },
],
},Any page using the default layout (explicitly, or implicitly — no layout in definePageMeta) will run ~/middleware/track-pageview.ts before rendering.
Named groups
Groups keep rules DRY when the same stack of middlewares is used from multiple layouts:
autoMiddleware: {
groups: {
auth: ['auth', 'verify-role'],
adminOnly: ['auth', 'verify-role', 'require-admin'],
},
rules: [
{ layouts: ['dashboard', 'dashboard/*'], middlewares: ['@auth'] },
{ layouts: ['admin', 'admin/*'], middlewares: ['@adminOnly'] },
],
},Group references are expanded at build time. An unknown @group name throws during nuxt prepare, so typos don't silently ship.
Glob patterns
Layouts support * (non-slash), ** (any), and ? (one non-slash char):
rules: [
{ layouts: ['dashboard/*'], middlewares: ['@auth'] }, // dashboard/x, dashboard/y
{ layouts: ['admin/**'], middlewares: ['@adminOnly'] }, // admin/x, admin/x/y, ...
{ layouts: ['settings-?'], middlewares: ['verify-role'] }, // settings-a, settings-b
],RegExp patterns
For anything more expressive, pass a RegExp directly:
rules: [
{ layouts: [/^(dashboard|portal)$/], middlewares: ['@auth'] },
{ layouts: [/^ops\//], middlewares: ['require-ops'] },
],Per-page extras
Append additional middlewares for one page without changing global rules. The field name (middlewares by default) is configurable via pageMetaField:
<script setup lang="ts">
definePageMeta({
layout: 'dashboard',
middlewares: ['force-2fa'], // runs AFTER @auth resolved from the layout rule
});
</script>Because the AutoMiddlewareName type is generated from your rules + groups, typos are compile-time errors.
Opt a page out entirely
<script setup lang="ts">
definePageMeta({
layout: 'dashboard',
skipAutoMiddleware: true, // no middleware runs, even though dashboard would match
});
</script>Catch-all layout
Use ** to run middleware for every layout (except pages with layout: false or skipAutoMiddleware: true):
rules: [
{ layouts: ['**'], middlewares: ['track-pageview'] },
{ layouts: ['dashboard', 'admin'], middlewares: ['@auth'] },
],When multiple rules match the same layout, their middlewares are concatenated in config order (duplicates are dropped).
Disabling per-page overrides
autoMiddleware: {
pageMetaField: false, // ignore definePageMeta({ middlewares: [...] })
rules: [ /* ... */ ],
},The typed registry is still generated (so AutoMiddlewareName remains useful elsewhere); the dispatcher just doesn't look at page meta.
Debug logs
autoMiddleware: { debug: true, rules: [ /* ... */ ] }With debug on, each navigation logs the resolved layout and the full ordered chain, plus whether any middleware returned false or a redirect.
Middleware return values
Middleware functions receive (to, from) exactly like standard Nuxt route middlewares:
| Return | Behaviour |
| ------------- | --------------------------------------------- |
| false | Stop the chain, allow navigation. |
| true / void | Continue to the next middleware. |
| anything else | Treated as a navigation result — chain stops. |
Example: an auth middleware that redirects to /login when the cookie is missing, otherwise lets the chain continue:
// ~/middleware/auth.ts
export default defineNuxtRouteMiddleware(() => {
const token = useCookie('token').value;
if (!token) return navigateTo('/login');
// undefined → continue to the next middleware
});Every middleware runs inside callWithNuxt(), so useNuxtApp() / composables work as usual.
Typed registry
The module emits #build/auto-middleware/types.d.ts with:
export type AutoMiddlewareName = 'auth' | 'verify-role' | 'require-admin' | 'log-activity';…plus an augmentation that adds middlewares?: AutoMiddlewareName[] and skipAutoMiddleware?: boolean to PageMeta. Both are auto-included in the Nuxt-generated nuxt.d.ts, so you don't need to import anything. You can import the type if you're authoring outside of definePageMeta:
import type { AutoMiddlewareName } from '#build/auto-middleware/types';
const stack: AutoMiddlewareName[] = ['auth', 'verify-role'];Matching rules
- A page with no explicit layout is treated as
default. - A page with
layout: falseis skipped entirely. - A page with
skipAutoMiddleware: trueis skipped entirely. - Layout pattern matching is anchored (
^...$) — partial matches do not count. - When multiple rules match, middlewares are concatenated in config order and deduped (first occurrence wins).
Module options reference
| Option | Type | Default | Purpose |
| --------------- | -------------------------- | --------------- | --------------------------------------------------------------------------------------------- |
| rules | AutoMiddlewareRule[] | [] | Layout → middlewares mapping. Required. |
| groups | Record<string, string[]> | {} | Named reusable middleware lists, referenced via @name inside rules. |
| debug | boolean | false | Log resolution + execution at runtime. |
| pageMetaField | string \| false | 'middlewares' | Page-meta key used to append extra middlewares. Set to false to disable per-page overrides. |
Exported types
import type {
AutoMiddlewareOptions,
AutoMiddlewareRule,
} from '@alikhalilll/nuxt-auto-middleware/types';License
MIT
