{"_id":"@aghlimi/next-middleware-chain","_rev":"4-c04c2e54e06126ebd8cf81d74c6a1712","name":"@aghlimi/next-middleware-chain","dist-tags":{"latest":"2.2.1"},"versions":{"1.0.0":{"name":"@aghlimi/next-middleware-chain","version":"1.0.0","keywords":["nextjs","middleware","typescript","proxy"],"license":"MIT","_id":"@aghlimi/next-middleware-chain@1.0.0","maintainers":[{"name":"aghlimi","email":"ahmed4aghlimi@gmail.com"}],"dist":{"shasum":"60c20593ba613c9b01a3d64b6c4d820f826dca4d","tarball":"https://registry.npmjs.org/@aghlimi/next-middleware-chain/-/next-middleware-chain-1.0.0.tgz","fileCount":6,"integrity":"sha512-m12kMZfxHVN75PjGJbO87BTytI6ufNr3syzkTjUAcjzM84fulNefGfa21dWU9YsZm6cqCHlqc8uNCo3eALyPHA==","signatures":[{"sig":"MEUCIGODwbwg44k8pUSjf1dglAOlADLGvkIthTlGimBTxTbHAiEAoGm5c09ghbx7fnsHnmPtDnSE+q7jhXwJNe9mTxzLcj4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":6498},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"build":"tsc -p tsconfig.json"},"_npmUser":{"name":"aghlimi","email":"ahmed4aghlimi@gmail.com"},"repository":{"url":"git+https://github.com/Aghlimi/nextjs-middleware.git","type":"git"},"description":"A tiny middleware-chain utility for Next.js request proxies.","directories":{},"_nodeVersion":"26.0.0","_hasShrinkwrap":false,"devDependencies":{"next":"^16.3.4","typescript":"^5.9.2"},"peerDependencies":{"next":">=16"},"_npmOperationalInternal":{"tmp":"tmp/next-middleware-chain_1.0.0_1788692703022_0.09952009911816395","host":"s3://npm-registry-packages-npm-production"}},"2.1.0":{"name":"@aghlimi/next-middleware-chain","version":"2.1.0","keywords":["nextjs","middleware","typescript","proxy"],"license":"MIT","_id":"@aghlimi/next-middleware-chain@2.1.0","maintainers":[{"name":"aghlimi","email":"ahmed4aghlimi@gmail.com"}],"dist":{"shasum":"cbffde7ff8aecb67a2d297991cb1b86a31d36714","tarball":"https://registry.npmjs.org/@aghlimi/next-middleware-chain/-/next-middleware-chain-2.1.0.tgz","fileCount":24,"integrity":"sha512-AL3qYX10UNl+15Dk/r/atGDoJv9CxHGtI39YCQn1hQH7NSJoezvlcKwNGI0oNJ6zhLTop6GlUs+Aj4QnBGIGGA==","signatures":[{"sig":"MEUCIQCtSIlU44fVcSXl2COQYyQ0ZaNl3Iph7wbfiOqBhVVfKAIgeD6YLzjtzrYpDn7bHObHHGmx+91O61UhE4y2kFcJoW0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":12594},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"build":"tsc -p tsconfig.json"},"_npmUser":{"name":"aghlimi","email":"ahmed4aghlimi@gmail.com"},"repository":{"url":"git+https://github.com/Aghlimi/nextjs-middleware.git","type":"git"},"description":"A tiny middleware-chain utility for Next.js request proxies.","directories":{},"_nodeVersion":"26.0.0","_hasShrinkwrap":false,"devDependencies":{"next":"^16.3.4","typescript":"^7.0.2"},"peerDependencies":{"next":">=16"},"_npmOperationalInternal":{"tmp":"tmp/next-middleware-chain_2.1.0_1788730946837_0.4399219676870001","host":"s3://npm-registry-packages-npm-production"}},"2.1.1":{"name":"@aghlimi/next-middleware-chain","version":"2.1.1","keywords":["nextjs","middleware","typescript","proxy"],"license":"MIT","_id":"@aghlimi/next-middleware-chain@2.1.1","maintainers":[{"name":"aghlimi","email":"ahmed4aghlimi@gmail.com"}],"dist":{"shasum":"4b87931f96990fded3743fbc9cf376e7115c8617","tarball":"https://registry.npmjs.org/@aghlimi/next-middleware-chain/-/next-middleware-chain-2.1.1.tgz","fileCount":24,"integrity":"sha512-1+DvdOqktvhsXG/HW924lCynWCP38bcpB8wJLYbeW7vnvf0TfjEszXOJHywx3SxbI1u2zZ8oI4UHhN4sTvN1KQ==","signatures":[{"sig":"MEYCIQC6jjFPw0fSW+chuOKghr3LXHAbc0Zia/ucTupSCvZyigIhANK3dyioqovcycWKh/HmclqnXjmMAT71RLhiF2p+ndAY","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":16996},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"build":"tsc -p tsconfig.json"},"_npmUser":{"name":"aghlimi","email":"ahmed4aghlimi@gmail.com"},"repository":{"url":"git+https://github.com/Aghlimi/nextjs-middleware.git","type":"git"},"description":"A tiny middleware-chain utility for Next.js request proxies.","directories":{},"_nodeVersion":"26.0.0","_hasShrinkwrap":false,"devDependencies":{"next":"^16.3.4","typescript":"^7.0.2"},"peerDependencies":{"next":">=16"},"_npmOperationalInternal":{"tmp":"tmp/next-middleware-chain_2.1.1_1788732047808_0.6583103473958867","host":"s3://npm-registry-packages-npm-production"}},"2.2.1":{"name":"@aghlimi/next-middleware-chain","version":"2.2.1","description":"A tiny middleware-chain utility for Next.js request proxies.","type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"keywords":["nextjs","middleware","typescript","proxy"],"license":"MIT","repository":{"type":"git","url":"git+https://github.com/Aghlimi/nextjs-middleware.git"},"peerDependencies":{"next":">=16"},"devDependencies":{"next":"^16.3.4","typescript":"^7.0.2"},"scripts":{"build":"tsc -p tsconfig.json"},"_nodeVersion":"26.0.0","_id":"@aghlimi/next-middleware-chain@2.2.1","dist":{"integrity":"sha512-Cx5+dyHNXW0DDQQ8sslHSrccb0/pouITwQkFCDFM/ThVHbvKUr4Fm4fu0xtlCj4L5dIT0kXihhCEv1D8wJGlQg==","shasum":"200f666021eba410105b3049a6c0b81d17d79753","tarball":"https://registry.npmjs.org/@aghlimi/next-middleware-chain/-/next-middleware-chain-2.2.1.tgz","fileCount":24,"unpackedSize":16833,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDdhuQzsK50S5YJtWPNeD6lJn7DlE4eW1+alJUDGX+fQwIgAss8N0D/kcBnPJDSrSfJbbDBjqc0xdEZb9XDxCJTYGI="}]},"_npmUser":{"name":"aghlimi","email":"ahmed4aghlimi@gmail.com"},"directories":{},"maintainers":[{"name":"aghlimi","email":"ahmed4aghlimi@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/next-middleware-chain_2.2.1_1788733158117_0.7503091167598432"},"_hasShrinkwrap":false}},"time":{"created":"2026-09-06T11:05:02.837Z","modified":"2026-09-06T22:19:18.409Z","1.0.0":"2026-09-06T11:05:03.157Z","2.1.0":"2026-09-06T21:42:26.972Z","2.1.1":"2026-09-06T22:00:47.933Z","2.2.1":"2026-09-06T22:19:18.252Z"},"license":"MIT","keywords":["nextjs","middleware","typescript","proxy"],"repository":{"type":"git","url":"git+https://github.com/Aghlimi/nextjs-middleware.git"},"description":"A tiny middleware-chain utility for Next.js request proxies.","maintainers":[{"name":"aghlimi","email":"ahmed4aghlimi@gmail.com"}],"readme":"# Next Middleware Chain\n\nA lightweight middleware chain for Next.js request proxies, with chainable route filters and an Express-style `next()` API.\n\nVersion **2.1.0** requires **Next.js 16 or later**.\n\n## Installation\n\n```bash\npnpm add @aghlimi/next-middleware-chain\n```\n\nOr with npm:\n\n```bash\nnpm install @aghlimi/next-middleware-chain\n```\n\n## Quick start\n\nRegister middleware once at module scope in your Next.js `proxy.ts`, then pass incoming requests to `executeMiddlewares()`:\n\n```ts\nimport { NextResponse, type NextRequest } from \"next/server\";\nimport {\n  executeMiddlewares,\n  setMiddleware,\n} from \"@aghlimi/next-middleware-chain\";\n\n// Run on every request handled by this proxy, except /health.\nsetMiddleware(async (request, next) => {\n  console.log(`${request.method} ${request.nextUrl.pathname}`);\n  next();\n})\n  .global()\n  .exclude(\"/health\");\n\n// Run on /api and its descendants, except /api/public and its descendants.\nsetMiddleware(async (request, next) => {\n  // Example response; replace this condition with your application logic.\n  if (request.nextUrl.pathname === \"/api/maintenance\") {\n    return NextResponse.json(\n      { message: \"Temporarily unavailable\" },\n      { status: 503 },\n    );\n  }\n\n  next();\n})\n  .forPath(\"/api\")\n  .excludePrefix(\"/api/public\");\n\nexport async function proxy(request: NextRequest) {\n  return executeMiddlewares(request);\n}\n```\n\nMiddleware runs in registration order. Use `next()` to continue the chain and forward the next middleware's response. Return a `NextResponse` without calling `next()` to stop the chain and respond immediately.\n\nIf no middleware matches, or the chain returns no response, `executeMiddlewares()` returns `NextResponse.next()`. Returning nothing without calling `next()` skips the remaining middleware and lets the request continue through Next.js.\n\n## Route filters\n\n`setMiddleware(fn)` returns a `Middleware` instance. All filter methods return the same instance, so they can be chained.\n\n| Method | Behavior | Example |\n| --- | --- | --- |\n| `.forPrefix(path)` | Include the path and its descendants. | `/api` matches `/api` and `/api/users`, but not `/apiary`. |\n| `.forPath(path)` | Include only the exact path. | `/auth` matches `/auth`, but not `/auth/login`. |\n| `.global()` | Include every path handled by the proxy. | Apply shared logging to all requests reaching the proxy. |\n| `.exclude(path)` | Exclude only the exact path. | `/health` is skipped, but `/health/details` is not. |\n| `.excludePrefix(path)` | Exclude the path and its descendants. | `/api/public` and `/api/public/posts` are skipped. |\n\n**Naming note for 2.1.0:** `forPath()` performs prefix matching at path-segment boundaries, while `forPrefix()` performs exact matching. The examples above reflect the current implementation.\n\n- Paths are literal strings, not regular expressions or wildcard patterns.\n- Trailing slashes are ignored: `/api` and `/api/` match equally.\n- Matching is case-sensitive and uses `request.nextUrl.pathname`; query strings are not included.\n- Exclusions take precedence over every inclusion, including `.global()`.\n- A registration needs `.forPath()`, `.forPrefix()`, or `.global()` to match requests.\n- Combining `.forPath()` and `.forPrefix()` includes requests matching either filter.\n- Each filter has one stored value. Repeating the same method replaces its previous path rather than adding another path.\n- Use `.global()` to include all paths. `.forPath(\"/\")` does not include ordinary descendant paths in this version.\n\nFor example, apply middleware to the `/api` subtree and the exact `/status` path:\n\n```ts\nsetMiddleware(async (request, next) => {\n  console.log(request.nextUrl.pathname);\n  return next();\n})\n  .forPath(\"/api\")\n  .forPrefix(\"/status\")\n  .exclude(\"/api/health\")\n  .excludePrefix(\"/api/internal\");\n```\n\n## Reusable middleware\n\nThe callback type can be inferred from `setMiddleware`. In 2.1.0, `MiddlewareFunction` is not exported from the package entry point.\n\n```ts\n// middlewares/logger.ts\nimport type { setMiddleware } from \"@aghlimi/next-middleware-chain\";\n\ntype MiddlewareFunction = Parameters<typeof setMiddleware>[0];\n\nexport const logger: MiddlewareFunction = async (request, next) => {\n  console.log(`${request.method} ${request.nextUrl.pathname}`);\n  return next();\n};\n```\n\nRegister the function in `proxy.ts`:\n\n```ts\nimport { setMiddleware } from \"@aghlimi/next-middleware-chain\";\nimport { logger } from \"./middlewares/logger\";\n\nsetMiddleware(logger).global().exclude(\"/health\");\n```\n\nRegistrations are keyed by function identity. Registering the same function again returns its existing `Middleware` instance and updates its filters; it does not create another chain entry or change its registration order. Use distinct callback functions for separate registrations.\n\n## API\n\nThe package exposes three named exports: `setMiddleware`, `executeMiddlewares`, and `Middleware`. There is no default export.\n\n### `setMiddleware(middlewareFunction): Middleware`\n\nRegisters an async callback and returns its filter builder. The callback signature is:\n\n```ts\nimport type { NextRequest, NextResponse } from \"next/server\";\n\ntype MiddlewareFunction = (\n  request: NextRequest,\n  next: () => Promise<NextResponse | void>,\n) => Promise<NextResponse | void>;\n```\n\nReturn `next()` or await and return its response to preserve downstream responses. Calling `next()` without returning or awaiting it can let the proxy finish before the downstream middleware completes.\n\n### `executeMiddlewares(request: NextRequest): Promise<NextResponse>`\n\nSelects middleware matching the request pathname and executes the chain in registration order. Errors thrown by middleware propagate to the caller.\n\n### `Middleware`\n\nThe class returned by `setMiddleware()`. It can also be constructed directly; construction registers the callback:\n\n```ts\nimport { Middleware } from \"@aghlimi/next-middleware-chain\";\n\nconst middleware = new Middleware(async (request, next) => {\n  console.log(request.nextUrl.pathname);\n  return next();\n}).forPath(\"/api\");\n\nmiddleware.matches(\"/api/users\"); // true\nmiddleware.matches(\"/about\"); // false\n```\n\n`matches(path: string): boolean` checks a pathname against the instance's inclusion and exclusion filters without executing the callback.\n\n## Migrating to 2.1.0\n\nThis version changes the registration API shown in the previous README. Existing callers must update their imports and route registrations.\n\nBefore:\n\n```ts\nimport setMiddleware from \"@aghlimi/next-middleware-chain\";\n\nsetMiddleware(\"^/api(?:/|$)\", async (request, next) => {\n  next();\n});\n```\n\nAfter:\n\n```ts\nimport { setMiddleware } from \"@aghlimi/next-middleware-chain\";\n\nsetMiddleware(async (request, next) => {\n  return next();\n}).forPath(\"/api\");\n```\n\nReplace regex registrations with the literal path filters described above. Replace package imports of `MiddlewareFunction` with the inferred callback type shown in the reusable middleware example.\n\n## License\n\nMIT\n","readmeFilename":""}