Extending Better Auth with global Rate Limiting
Source: Dev.to
TL;DR
Install better-auth-rate-limiter, add a single plugin call, and every route in your app—auth endpoints, AI routes, payment APIs, etc.—is rate‑limited. Choose memory, database, or Redis as the backend. Full TypeScript support.
Installation
npm install better-auth-rate-limiter
# or
pnpm add better-auth-rate-limiter
Adding the Plugin
import { betterAuth } from "better-auth";
import { rateLimiter } from "better-auth-rate-limiter";
export const auth = betterAuth({
plugins: [
rateLimiter({
window: 60, // seconds
max: 100, // requests per window
storage: "memory",
detection: "ip",
}),
],
});
The auth instance now acts as the central rate‑limiting authority for all routes you point it at.
Storage Backends
Memory (default)
rateLimiter({ storage: "memory" })
Zero dependencies, works immediately. Ideal for single‑instance apps or development.
Database
rateLimiter({ storage: "database" })
Persists rate‑limit state across restarts and works across multiple server instances using your existing Better Auth database.
Redis (secondary storage)
import { Redis } from "ioredis";
const redis = new Redis();
export const auth = betterAuth({
secondaryStorage: {
get: (key) => redis.get(key),
set: (key, value, ttl) => redis.set(key, value, "EX", ttl ?? 3600),
delete: (key) => redis.del(key),
},
plugins: [
rateLimiter({ storage: "secondary-storage" }),
],
});
Best for high‑traffic production apps or horizontally scaled deployments.
Detection Modes
// IP‑based (default, for unauthenticated traffic)
rateLimiter({ detection: "ip" })
// User‑based (authenticated users only)
rateLimiter({ detection: "user" })
// Both: user ID when logged in, IP when not
rateLimiter({ detection: "ip-and-user" })
ip-and-user provides stricter limits for authenticated users while still protecting anonymous traffic.
Custom Rules & Wildcards
rateLimiter({
window: 60,
max: 100,
customRules: {
// Brute‑force protection for sign‑in
"/api/auth/sign-in": { window: 60, max: 5 },
// Bot protection for sign‑up
"/api/auth/sign-up": { window: 3600, max: 3 },
// Limit all AI endpoints
"/api/ai/*": { window: 60, max: 10 },
// Disable rate limiting for health checks
"/api/health": false,
},
})
Wildcard support: * matches a single segment, ** matches multiple segments.
Using checkRateLimit in Route Handlers
// src/app/api/generate/route.ts
import { auth } from "@/lib/auth";
import { NextRequest, NextResponse } from "next/server";
export async function POST(request: NextRequest) {
const result = await auth.api.checkRateLimit({
headers: request.headers,
body: { path: request.nextUrl.pathname },
});
if (!result.success) {
return NextResponse.json(
{ error: "Too many requests", retryAfter: result.retryAfter },
{ status: 429 }
);
}
// Your protected logic (LLM call, payment, etc.)
}
The path you provide is matched against customRules, allowing per‑endpoint limits without extra setup.
Middleware Integration
// src/middleware.ts
import { auth } from "@/lib/auth";
import { NextRequest, NextResponse } from "next/server";
export async function middleware(request: NextRequest) {
const path = request.nextUrl.pathname;
// Skip Better Auth's own routes (already handled)
if (path.startsWith("/api/auth")) return NextResponse.next();
const result = await auth.api.checkRateLimit({
headers: request.headers,
body: { path },
});
if (!result.success) {
return NextResponse.json(
{ error: "Too many requests", retryAfter: result.retryAfter },
{ status: 429 }
);
}
const response = NextResponse.next();
response.headers.set("X-RateLimit-Limit", String(result.limit));
response.headers.set("X-RateLimit-Remaining", String(result.remaining));
return response;
}
export const config = {
matcher: ["/api/:path*"],
};
All API requests are checked before reaching route handlers.
Response Headers
Every successful response includes:
X-RateLimit-Limit– total allowed requests per windowX-RateLimit-Remaining– requests left in the current windowX-RateLimit-Reset(if implemented) – time until the window resets
Clients can read these headers to avoid guessing.
Features Summary
| Feature | Details |
|---|---|
| Storage backends | memory, database, Redis (via secondary storage) |
| Detection modes | IP, user, or both (ip-and-user) |
| Per‑route custom rules | Wildcard support (*, **) |
| Standard rate‑limit headers | X-RateLimit-* automatically added |
| TypeScript | Full type safety |
| License | MIT (community plugin) |
Links
- npm: https://www.npmjs.com/package/better-auth-rate-limiter
- GitHub: https://github.com/your-repo/better-auth-rate-limiter
Have feedback or a use case not covered? Open an issue on GitHub.