npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

icetea-middleware

v0.1.15

Published

A simple middleware for nextjs

Readme

Ice Tea Middleware

A lightweight, flexible middleware pipeline for Next.js applications. Ice Tea Middleware provides a clean and extensible way to manage request/response middleware in your Next.js projects using class-based middleware instances with signal support.

Features

  • Simple & Clean API – Register and execute middlewares with minimal boilerplate
  • Signal-Based Early Termination – Halt middleware execution when needed using signals
  • Route-Based Filtering – Conditionally execute middlewares based on request matching
  • Zero-Config Setup – Works out of the box with Next.js App Router and Pages Router
  • Response Migration – Seamlessly transfer headers and cookies between responses
  • Core Middleware Support – Execute essential middlewares on every request regardless of route
  • Async/Await Ready – Built for modern asynchronous middleware execution

Installation

npm install icetea-middleware
yarn add icetea-middleware
pnpm add icetea-middleware

Quick Start

1. Create a Middleware Instance

Each middleware is an object implementing the MiddlewareInstance interface:

import { NextRequest, NextResponse } from "next/server";
import { ISignal } from "icetea-middleware";

export const loggerMiddleware = {
  name: "logger",
  route: () => true, // applies to all requests
  executor: async (
    request: NextRequest,
    response: NextResponse,
    signal: ISignal,
  ) => {
    console.log(`[${request.method}] ${request.nextUrl.pathname}`);
    return response;
  },
};

2. Set Up the Middleware Pipeline

Create a middleware.ts file at the root of your Next.js project:

// middleware.ts
import { NextRequest } from "next/server";
import { MiddlewareWrapper } from "icetea-middleware";

export async function middleware(request: NextRequest) {
  const wrapper = new MiddlewareWrapper(request);

  wrapper.register([
    loggerMiddleware,
    // ... other middlewares
  ]);

  return await wrapper.execute();
}

API Reference

MiddlewareWrapper

The core class that orchestrates middleware execution.

constructor(request: NextRequest)

Creates a new middleware pipeline instance.

const wrapper = new MiddlewareWrapper(request);

register(middlewares: MiddlewareInstance[]): void

Registers an array of middlewares. Core middlewares (those with a boolean route) are executed on every request, while route-based middlewares are filtered by their route function.

wrapper.register([
  { name: "auth", route: true, executor: async (req, res, signal) => res },
  {
    name: "logging",
    route: (req) => req.nextUrl.pathname.startsWith("/api"),
    executor: async (req, res, signal) => res,
  },
]);

execute(): Promise<NextResponse | null>

Executes the registered middlewares sequentially. Returns a NextResponse or null.

const response = await wrapper.execute();

static switchResponse(newResponse: NextResponse, response: NextResponse): void

Copies headers and cookies from one response to another. Useful when you need to create a new response but preserve existing headers/cookies.

const newResponse = NextResponse.redirect(new URL("/login", request.url));
MiddlewareWrapper.switchResponse(newResponse, response);

Signal

A simple signal mechanism to stop middleware execution early.

import { Signal } from "icetea-middleware";

const signal = new Signal();
signal.set(); // triggers the signal
const isStopped = signal.get(); // boolean

Types

MiddlewareInstance

interface MiddlewareInstance {
  name: string;
  route: boolean | ((request: NextRequest) => boolean);
  executor: (
    request: NextRequest,
    response: NextResponse,
    signal: ISignal,
  ) => Promise<NextResponse>;
}

| Property | Type | Description | | ---------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------- | | name | string | A descriptive name for the middleware (used for debugging/logging) | | route | boolean \| ((request) => boolean) | If true, runs on every request. If a function, executes when it returns true | | executor | (request, response, signal) => Promise<NextResponse> | The middleware logic. Receive the request, current response, and a signal to stop execution |

ISignal

interface ISignal {
  set: () => void;
  get: () => boolean;
}

Examples

Authentication Middleware

import { NextRequest, NextResponse } from "next/server";
import { ISignal } from "icetea-middleware";

export const authMiddleware = {
  name: "auth",
  route: (request: NextRequest) => {
    const protectedPaths = ["/dashboard", "/profile", "/admin"];
    return protectedPaths.some((path) =>
      request.nextUrl.pathname.startsWith(path),
    );
  },
  executor: async (
    request: NextRequest,
    response: NextResponse,
    signal: ISignal,
  ) => {
    const token = request.cookies.get("session-token");

    if (!token) {
      signal.set();
      return NextResponse.redirect(new URL("/login", request.url));
    }

    return response;
  },
};

Rate Limiting Middleware

import { NextRequest, NextResponse } from "next/server";
import { ISignal } from "icetea-middleware";

const rateLimit = new Map<string, { count: number; timestamp: number }>();

export const rateLimitMiddleware = {
  name: "rate-limit",
  route: (request: NextRequest) => request.nextUrl.pathname.startsWith("/api"),
  executor: async (
    request: NextRequest,
    response: NextResponse,
    signal: ISignal,
  ) => {
    const ip = request.ip ?? "unknown";
    const now = Date.now();
    const windowMs = 60_000; // 1 minute
    const maxRequests = 100;

    const entry = rateLimit.get(ip);

    if (!entry || now - entry.timestamp > windowMs) {
      rateLimit.set(ip, { count: 1, timestamp: now });
      return response;
    }

    entry.count++;

    if (entry.count > maxRequests) {
      signal.set();
      return new NextResponse("Too Many Requests", { status: 429 });
    }

    return response;
  },
};

How It Works

  1. InitializationMiddlewareWrapper is instantiated with the incoming NextRequest.
  2. Registration – Middlewares are registered via register(). They are split into two groups:
    • Core middlewares (route: boolean) – Always executed.
    • Route middlewares (route: function) – Executed only when the route function returns true for the current request.
  3. Executionexecute() runs each middleware in order, passing the request, current response, and a Signal instance.
  4. Early Termination – If any middleware calls signal.set(), the pipeline stops immediately and returns the current response.
  5. Response – If execution completes without interruption, the default NextResponse.next() is returned.

License

MIT © De Wibisana