g14o
Packages@g14o/ratelimit-nextjs

Setup

Next.js App Router routes and per-user rate limiting.

Create a client

lib/ratelimit.ts
import { createRateLimit } from "@g14o/ratelimit-nextjs";
import { logger } from "@/lib/logger";
import { env } from "@/lib/env";

export const { withRateLimit, checkRateLimit, withUserRateLimit } =
  createRateLimit({
    redis: {
      url: env.UPSTASH_REDIS_REST_URL,
      token: env.UPSTASH_REDIS_REST_TOKEN,
    },
    logger,
  });
lib/ratelimit.ts
import { createRateLimit } from "@g14o/ratelimit-nextjs";
import { upstashStore } from "@g14o/ratelimit-nextjs/upstash";
import { env } from "@/lib/env";
import { logger } from "@/lib/logger";

export const { withRateLimit } = createRateLimit({
  store: upstashStore({
    url: env.UPSTASH_REDIS_REST_URL,
    token: env.UPSTASH_REDIS_REST_TOKEN,
  }),
  logger,
});

Store helpers (memoryStore, upstashStore, createStore, defineStore) are exported from @g14o/ratelimit-nextjs and its /memory and /upstash subpaths. store and redis are mutually exclusive.

App Router route

Built-in tier limits are listed in @g14o/ratelimit setup — default tiers.

app/api/chat/route.ts
import { NextResponse } from "next/server";
import { withRateLimit } from "@/lib/rate-limit";

export const POST = withRateLimit(
  async (req) => NextResponse.json({ ok: true }),
  { tier: "moderate" }
);

Per-user limits

Use a verified identity from your auth provider (Better Auth, NextAuth, Clerk, etc.) — not client-controlled headers.

app/api/chat/route.ts
import { NextResponse } from "next/server";
import { withUserRateLimit } from "@/lib/rate-limit";
import { getSession } from "@/lib/auth";

export const POST = withUserRateLimit(
  async (req) => NextResponse.json({ ok: true }),
  async (req) => {
    const session = await getSession(req);
    return session?.user.id ?? null;
  },
  { tier: "auth" }
);

Middleware

Use checkRateLimit from your middleware when you need manual control:

app/api/chat/route.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { checkRateLimit } from "@/lib/rate-limit";

export async function POST(req: NextRequest) {
  const result = await checkRateLimit(req, { tier: "strict" });
  if (!result.ok) {
    return NextResponse.json({ error: "Too many requests" }, { status: 429 });
  }
}

See @g14o/ratelimit middleware patterns for more examples (adapt Response to NextResponse).

On this page