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

ratelimit-next

v0.1.1

Published

Rate limiting for Next.js. Zero dependencies.

Readme

ratelimit-next

Rate limiting for Next.js. Zero dependencies.

Declarative rules, type-safe API, pluggable stores. Works in Edge Runtime, Node.js, and serverless.

Install

npm install ratelimit-next

Quick Start

1. Define rules

// lib/rate-limit.ts
import { createFloodgate } from "ratelimit-next";

export const gate = createFloodgate({
  rules: {
    api: { limit: 100, window: "1m" },
    auth: { limit: 5, window: "15m", algorithm: "fixed-window" },
    uploads: { limit: 10, window: "1h", algorithm: "token-bucket" },
  },
});

2. Protect routes

// app/api/data/route.ts
import { withRateLimit } from "ratelimit-next/next";
import { gate } from "@/lib/rate-limit";

export const GET = withRateLimit(gate, "api", async (request) => {
  return Response.json({ data: "hello" });
});

3. Done

Clients get standard rate limit headers (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset) on every response and a 429 with Retry-After when limited.

Algorithms

Fixed Window

Divides time into fixed intervals and counts requests per interval. Simple and predictable, but allows bursts at window boundaries.

{ limit: 100, window: "1m", algorithm: "fixed-window" }

Sliding Window (default)

Approximates a true sliding window by weighting the previous interval's count. Smoother than fixed window with minimal overhead.

{ limit: 100, window: "1m", algorithm: "sliding-window" }

Token Bucket

Tokens refill at a steady rate. Allows short bursts while maintaining an average rate. Best for APIs where occasional spikes are acceptable.

{ limit: 100, window: "1m", algorithm: "token-bucket" }

Window Format

Duration strings: "10s" (seconds), "5m" (minutes), "1h" (hours), "1d" (days).

Next.js Middleware

Apply rate limiting globally via Next.js middleware:

// middleware.ts
import { createFloodgate } from "ratelimit-next";
import { createRateLimitMiddleware } from "ratelimit-next/next";

const gate = createFloodgate({
  rules: { api: { limit: 100, window: "1m" } },
});

export default createRateLimitMiddleware(gate, {
  rule: "api",
  paths: ["/api/"],
});

export const config = { matcher: "/api/:path*" };

API Route Protection

Three styles to choose from:

withRateLimit (HOF)

import { withRateLimit } from "ratelimit-next/next";

export const GET = withRateLimit(gate, "api", async (request) => {
  return Response.json({ ok: true });
});

rateLimit (guard)

import { rateLimit } from "ratelimit-next/next";

export async function GET(request: Request) {
  const limited = await rateLimit(gate, "api", request);
  if (limited) return limited;

  return Response.json({ ok: true });
}

gate.limit (throws)

import { gate } from "@/lib/rate-limit";
import { RateLimitError } from "ratelimit-next";

export async function GET(request: Request) {
  try {
    await gate.limit("api", ip(request));
    return Response.json({ ok: true });
  } catch (e) {
    if (e instanceof RateLimitError) {
      return Response.json({ error: "Too many requests" }, { status: 429 });
    }
    throw e;
  }
}

React

Client-side components for rate-limit-aware UIs:

import { RateLimited, useRateLimit, RateLimitProvider } from "ratelimit-next/react";

// Wrap your app (optional — configures the check endpoint)
<RateLimitProvider endpoint="/api/rate-limit">
  <App />
</RateLimitProvider>

// Declarative
<RateLimited rule="api-calls" fallback={<p>Too many requests. Please wait.</p>}>
  <MyForm />
</RateLimited>

// Hook
function SubmitButton() {
  const { allowed, remaining, check } = useRateLimit("api-calls");
  return (
    <button disabled={!allowed} onClick={() => { /* submit */ await check(); }}>
      Submit ({remaining} left)
    </button>
  );
}

The React components call GET /api/rate-limit?rule=<name> to check status. Implement this endpoint with your floodgate instance:

// app/api/rate-limit/route.ts
export async function GET(request: Request) {
  const url = new URL(request.url);
  const rule = url.searchParams.get("rule") ?? "api";
  const result = await gate.check(rule);
  return Response.json(result);
}

Stores

Memory (default)

Zero-config, works everywhere. State is lost on restart. Good for development and single-instance deployments.

import { createFloodgate, MemoryStore } from "ratelimit-next";

const gate = createFloodgate({
  rules: { api: { limit: 100, window: "1m" } },
  store: new MemoryStore(), // this is the default
});

Redis

For multi-instance deployments. Requires ioredis.

import Redis from "ioredis";
import { createFloodgate } from "ratelimit-next";
import { createRedisStore } from "ratelimit-next/adapters/redis";

const gate = createFloodgate({
  rules: { api: { limit: 100, window: "1m" } },
  store: createRedisStore(new Redis(process.env.REDIS_URL)),
});

Vercel KV

For Vercel deployments. Requires @vercel/kv.

import { kv } from "@vercel/kv";
import { createFloodgate } from "ratelimit-next";
import { createVercelKVStore } from "ratelimit-next/adapters/vercel-kv";

const gate = createFloodgate({
  rules: { api: { limit: 100, window: "1m" } },
  store: createVercelKVStore(kv),
});

Custom Key Resolvers

By default, rate limits are keyed by IP address. Customize this:

const gate = createFloodgate({
  rules: { api: { limit: 100, window: "1m" } },
  keyResolver: (request) => {
    // Key by API token
    return request.headers.get("Authorization") ?? "anonymous";
  },
});

Or pass keys directly:

const result = await gate.check("api", userId);

API Reference

createFloodgate(config)

Returns a Floodgate instance with:

| Method | Description | |---|---| | check(rule, key?) | Check limit, returns RateLimitResult | | limit(rule, key?) | Check limit, throws RateLimitError if exceeded | | reset(rule, key) | Clear limit for a key | | headers(result) | Generate HTTP headers from a result |

RateLimitResult

{
  allowed: boolean;
  limit: number;
  remaining: number;
  reset: number;       // timestamp ms
  retryAfter?: number; // seconds (present when blocked)
}

License

MIT