One entry point, every middleware

Next.js gives you one middleware file. NEMO lets you keep writing small, named middleware functions and dispatches them by path, so the entry point stays a routing table instead of growing into a switch statement.

GitHub
middleware.ts
import { createNEMO } from "@zanreal/nemo";

export const middleware = createNEMO({
  "/dashboard/:path*": [requireSession],
  "/api/:path*": [rateLimit, requireApiKey],
});

Maintained by ZanReal as part of its OSS Program

NEMO is a company project rather than one person's side project: the package, the repository and the release process belong to ZanReal, and the licence stays MIT. What that means.

First, let the code get the word out

The same routing logic, written by hand and written with NEMO.

middleware.ts
import { NextResponse, type NextRequest } from "next/server";

export const middleware = async (req: NextRequest) => {
  let user = undefined;
  let team = undefined;
  const token = req.headers.get("token");

  if (req.nextUrl.pathname.startsWith("/auth")) {
    user = await getUserByToken(token);

    if (!user) {
      return NextResponse.redirect("/login");
    }

    return NextResponse.next();
  }

  if (req.nextUrl.pathname.startsWith("/team/") || req.nextUrl.pathname.startsWith("/t/")) {
    user = await getUserByToken(token);

    if (!user) {
      return NextResponse.redirect("/login");
    }

    team = await getTeamBySlug(req.nextUrl.searchParams.get("slug"));

    if (!team) {
      return NextResponse.redirect("/");
    }

    return NextResponse.next();
  }

  return NextResponse.next();
};
middleware.ts
import { createNEMO } from "@zanreal/nemo";
import { auth } from "@/app/(auth)/auth/_middleware";
import { team } from "@/app/(team)/team/_middleware";

const globalMiddlewares = {
  before: auth, // OR: [auth, ...]
};

const middlewares = {
  "/auth/:path*": auth,
  "/(team|t)/:slug/:path*": team, // OR: [team, ...]
};

export const middleware = createNEMO(middlewares, globalMiddlewares);

Every branch that used to be a path check is a key. The functions themselves live next to the routes they guard, and the entry point stops growing.

Simplify your middlewares now

Supports

Dynamic segments

Run your middleware for any route or dynamic segment.

Functions chaining

Chain multiple middlewares for a single group.

Shared context

Functions can share context between each other.

Open source

Open-source

Something is missing? Just add it, or post an issue on GitHub.

Developer experience

Typed end to end, no build step of its own, and an API small enough to hold in your head.

Want to help with this project?

Review existing issues, make PRs with what is missing.

Other versions

Each major keeps its own documentation. v1 and v2 stay published for anyone who has not upgraded yet.

Was this helpful?

M↓supported.