Next Compose Middleware

1.0.0 · active · verified Wed Apr 22

next-compose-middleware is a library designed to simplify the creation of complex and declarative middleware for Next.js applications, particularly leveraging Next.js's Edge Runtime middleware. It enables developers to construct highly readable and maintainable middleware logic by composing multiple functions. The current stable version is 2.0.4, indicating active development with incremental releases. Key differentiators include its path-based middleware execution, allowing for "Nested Middleware" behavior, and the ability to compose functions with early exit mechanisms (`breakAll`, `breakOnce`) for fine-grained control over execution flow. This approach helps in organizing middleware into logical, reusable units, enhancing maintainability for applications with intricate authorization, authentication, or request transformation requirements in a single `middleware.ts` file.

Common errors

Warnings

Install

Imports

Quickstart

Demonstrates how to set up `next-compose-middleware` with path-based execution for different routes and compose multiple middleware functions, including an example of conditional logic for early exit.

import { NextRequest, NextResponse } from 'next/server';
import { composeMiddleware, ComposableMiddleware } from 'next-compose-middleware';

// Example middleware functions
const root1: ComposableMiddleware = async (req, res) => {
  console.log('Executing root1 for path:', req.nextUrl.pathname);
  // Example: Modify response headers or cookies
  res.headers.set('X-Root-Middleware-1', 'processed');
  return res;
};

const root2: ComposableMiddleware = async (req, res) => {
  console.log('Executing root2 for path:', req.nextUrl.pathname);
  return res;
};

const foo: ComposableMiddleware = async (req, res) => {
  console.log('Executing foo for path:', req.nextUrl.pathname);
  return res;
};

const fooBar: ComposableMiddleware = async (req, res) => {
  console.log('Executing fooBar for path:', req.nextUrl.pathname);
  // Early exit example: if a condition is met, stop further middleware
  if (req.nextUrl.searchParams.has('exit')) {
    console.log('Early exiting from fooBar!');
    return res; // Returning res without breakAll/breakOnce continues chain, but no further changes here.
  }
  return res;
};

const fooId: ComposableMiddleware = async (req, res) => {
  const id = req.nextUrl.pathname.split('/')[2];
  console.log('Executing fooId for path:', req.nextUrl.pathname, 'ID:', id);
  return res;
};

const fooIdBaz: ComposableMiddleware = async (req, res) => {
  console.log('Executing fooIdBaz for path:', req.nextUrl.pathname);
  return res;
};

const fooQux: ComposableMiddleware = async (req, res) => {
  console.log('Executing fooQux for path:', req.nextUrl.pathname);
  return res;
};

export default async function middleware(req: NextRequest) {
  console.log(`\n--- Incoming Request for ${req.nextUrl.pathname} ---`);
  
  // Compose middleware based on paths
  return composeMiddleware(req, NextResponse.next(), {
    scripts: [root1, root2], // Applied to all matching paths
    '/foo': {
      scripts: [foo], // Applied to /foo and its children
      '/bar': {
        scripts: [fooBar], // Applied to /foo/bar and its children
      },
      '/[id]': { // Dynamic segment
        scripts: [fooId], // Applied to /foo/:id and its children
        '/baz': [fooIdBaz] // Applied to /foo/:id/baz
      },
      '/qux': [fooQux] // Applied to /foo/qux
    }
  });
}

view raw JSON →