nextjs-middleware-stack
v2.0.2
Published
Composable route-based middleware for Next.js Middleware.
Maintainers
Readme
🚣 middlewareStack – A Composable Middleware Router for Next.js
A lightweight, composable middleware stack for Next.js that supports:
stringroute patterns (with dynamic parameters & wildcards)- Raw
RegExp - Sync/async predicate functions
- Strict
pipe()composition API
Perfect for authentication gates, logging, redirects, feature flags, i18n integration, and layered request logic in Next.js Edge or Node runtimes.
✨ Features
✅ Clean
pipe()API (no tuple arrays)✅ String pattern matching via
compare-path:paramdynamic segments**greedy wildcards
✅ Native
RegExpsupport✅ Sync or async predicate matching
✅ Middleware short-circuiting
✅ Edge & Node compatible
✅ Type-safe enforcement of
pipe()usage
📦 Installation
npm install nextjs-middleware-stack🧠 API
pipe(pattern, handler)
Creates a middleware pipe.
export type PatternFn = (req: Request) => boolean | Promise<boolean>
export type Pattern = PatternFn | string | RegExpParameters
pattern- string route shape (
/users/:id) RegExp- predicate function
(req) => boolean | Promise<boolean>
- string route shape (
handler(req: Request) => Response | void | Promise<Response | void>
pipe()must be used. Raw[pattern, handler]tuples are not supported.
middlewareStack(pipes)
middlewareStack(pipes: Pipe[])Creates a Next.js-compatible middleware handler from an ordered list of pipe() calls.
Returns:
;(req: Request) => Promise<Response | void>Ready to export as default from middleware.ts.
🚀 Example (Authentication Middleware)
// apps/example-app/src/middleware.ts
import { NextResponse } from 'next/server'
import { middlewareStack, pipe } from 'nextjs-middleware-stack'
import { validateAuthToken } from './utils/validate-jwt'
export default middlewareStack([
pipe(/^\/dashboard\/.*/, async (req) => {
const token = req.cookies.get('AUTH_TOKEN')?.value
const isValid = token && (await validateAuthToken(token))
if (!isValid) {
return NextResponse.redirect(new URL('/login', req.url))
}
}),
pipe('/public/:page', async () => {
console.log('Public page hit')
}),
// Always run last
pipe(
() => true,
async () => {
console.log('Final middleware')
}
),
])📊 Supported Path Shapes
String patterns are powered by compare-path.
| Pattern | Matches |
| --------------- | ------------------------- |
| /user/:id | /user/42 |
| /users/[id] | /users/42 |
| /docs/**/edit | /docs/api/v1/intro/edit |
| /a/:x/**/b/:y | /a/1/foo/bar/b/2 |
Use RegExp for advanced matching.
Use a predicate function when matching depends on:
- Cookies
- Headers
- Geo
- Runtime conditions
- Feature flags
🧪 Example: String Matching
import { middlewareStack, pipe } from 'nextjs-middleware-stack'
export default middlewareStack([
pipe('/users/:id', async (req) => {
// Handle specific user route
}),
pipe('cars/:id', async () => {
// Match /cars/123
}),
pipe('hello/**', async () => {
// Match anything under /hello/
}),
])🧪 Example: Predicate Function
import { middlewareStack, pipe } from 'nextjs-middleware-stack'
export default middlewareStack([
pipe(
async (req) => {
return req.url.includes('/api/') && !!req.headers.get('authorization')
},
async () => {
console.log('Authenticated API request')
}
),
])🧹 Next.js Integration
You must also export config to control where middleware runs:
export const config = {
matcher: [
'/((?!_next|.*\\..*).*)', // Skip static assets
'/(api|trpc)(.*)', // Always include API routes
],
}🧠 How It Works
For each request:
- Iterate through pipes in order.
- Evaluate the
pattern. - If matched → execute the
handler. - If the handler returns a
Response, execution stops. - Otherwise → continue to the next pipe.
Middleware order matters.
💡 Tips
- Place authentication gates before public routes.
- Put always-run middleware last:
pipe(() => true, someMiddleware)- Prefer string shapes for readability.
- Use predicate functions for request-aware logic.
- Use
RegExponly when necessary.
⚠️ Breaking Change (v2+)
middlewareStack now accepts only pipe() entries.
Old style:
middlewareStack([[/regex/, handler]])is no longer supported.
Migration:
middlewareStack([pipe(/regex/, handler)])Islam Yamor.
