Examples

See Universal Rate Limiter in action with real code examples

Live Playground

Try the rate limiter yourself! Click the button below to test a limiter that allows 3 requests per 10 seconds.

Configuration

Max Requests:3
Time Window:10 seconds
Storage:MemoryStorage

Console Output

Click "Test Request" to see output...

Simple API Rate Limiting

Basic rate limiting for API endpoints with a fixed limit per time window.

simple-api-limiting.ts
1import { createRateLimiter, MemoryStorage } from 'universal-rate-limiter';
2
3const apiLimiter = createRateLimiter({
4  key: 'api-endpoint',
5  max: 100,
6  window: '15m',
7  storage: new MemoryStorage()
8});
9
10// In your API handler
11async function handleRequest(req, res) {
12  const result = await apiLimiter.check();
13
14  if (!result.allowed) {
15    return res.status(429).json({
16      error: 'Too many requests',
17      retryAfter: result.retryAfter
18    });
19  }
20
21  // Process request
22  res.json({ data: 'Success', remaining: result.remaining });
23}

Per-User Rate Limiting

Dynamic rate limiting based on user ID or IP address.

per-user-limiting.ts
1import { createRateLimiter, MemoryStorage } from 'universal-rate-limiter';
2
3const userLimiter = createRateLimiter({
4  key: (req) => `user-${req.userId || req.ip}`,
5  max: 50,
6  window: '1h',
7  storage: new MemoryStorage()
8});
9
10// In your Express app
11app.use(async (req, res, next) => {
12  const result = await userLimiter.check(req);
13
14  if (!result.allowed) {
15    return res.status(429).json({
16      error: 'Too many requests from this user',
17      retryAfter: result.retryAfter
18    });
19  }
20
21  // Add rate limit info to response headers
22  res.setHeader('X-RateLimit-Limit', '50');
23  res.setHeader('X-RateLimit-Remaining', result.remaining.toString());
24
25  next();
26});

Express: Global + Route-Specific Limits

Combine global rate limiting with stricter limits for specific sensitive routes.

express-multi-tier.ts
1import express from 'express';
2import { rateLimitExpress } from 'universal-rate-limiter';
3
4const app = express();
5
6// Global rate limit: 1000 requests per hour
7app.use(rateLimitExpress({
8  key: (req) => `global-${req.ip}`,
9  max: 1000,
10  window: '1h'
11}));
12
13// Stricter limit for login endpoint: 5 attempts per 15 minutes
14app.post('/api/auth/login',
15  rateLimitExpress({
16    key: (req) => `login-${req.ip}`,
17    max: 5,
18    window: '15m'
19  }),
20  (req, res) => {
21    // Handle login
22  }
23);
24
25// Stricter limit for registration: 3 per day
26app.post('/api/auth/register',
27  rateLimitExpress({
28    key: (req) => `register-${req.ip}`,
29    max: 3,
30    window: '1d'
31  }),
32  (req, res) => {
33    // Handle registration
34  }
35);

Next.js API Routes

Rate limiting in Next.js API routes with proper error handling.

app/api/data/route.ts
1import { NextRequest, NextResponse } from 'next/server';
2import { createRateLimiter, MemoryStorage } from 'universal-rate-limiter';
3
4const limiter = createRateLimiter({
5  key: (req: NextRequest) => req.ip ?? 'anonymous',
6  max: 10,
7  window: '1m',
8  storage: new MemoryStorage()
9});
10
11export async function GET(request: NextRequest) {
12  const result = await limiter.check(request);
13
14  if (!result.allowed) {
15    return NextResponse.json(
16      {
17        error: 'Too many requests',
18        retryAfter: Math.ceil(result.retryAfter / 1000) // Convert to seconds
19      },
20      {
21        status: 429,
22        headers: {
23          'Retry-After': Math.ceil(result.retryAfter / 1000).toString()
24        }
25      }
26    );
27  }
28
29  // Process request
30  return NextResponse.json({
31    message: 'Success',
32    rateLimit: {
33      remaining: result.remaining,
34      limit: 10
35    }
36  });
37}

React Hook in Client Component

Limit user actions on the client side, like button clicks or form submissions.

ContactForm.tsx
1'use client';
2
3import { useState } from 'react';
4import { useRateLimit } from 'universal-rate-limiter';
5
6export function ContactForm() {
7  const [status, setStatus] = useState('');
8  const { allowed, remaining, attempt } = useRateLimit('contact-form', {
9    max: 3,
10    window: '1h'
11  });
12
13  const handleSubmit = async (e: React.FormEvent) => {
14    e.preventDefault();
15
16    if (!allowed) {
17      setStatus(`Too many submissions. You have ${remaining} remaining.`);
18      return;
19    }
20
21    await attempt();
22
23    // Submit form
24    try {
25      await fetch('/api/contact', {
26        method: 'POST',
27        body: JSON.stringify({ /* form data */ })
28      });
29      setStatus(`Form submitted! ${remaining - 1} submissions remaining.`);
30    } catch (error) {
31      setStatus('Error submitting form');
32    }
33  };
34
35  return (
36    <form onSubmit={handleSubmit}>
37      <input type="email" required />
38      <textarea required />
39      <button type="submit" disabled={!allowed}>
40        Submit ({remaining} remaining)
41      </button>
42      {status && <p>{status}</p>}
43    </form>
44  );
45}

Multiple Time Windows

Combine multiple rate limiters for different time windows (e.g., per second, per minute, per hour).

multi-window-limiting.ts
1import { createRateLimiter, MemoryStorage } from 'universal-rate-limiter';
2
3// Create multiple limiters for different time windows
4const perSecondLimiter = createRateLimiter({
5  key: (req) => `second-${req.ip}`,
6  max: 10,
7  window: '1s',
8  storage: new MemoryStorage()
9});
10
11const perMinuteLimiter = createRateLimiter({
12  key: (req) => `minute-${req.ip}`,
13  max: 100,
14  window: '1m',
15  storage: new MemoryStorage()
16});
17
18const perHourLimiter = createRateLimiter({
19  key: (req) => `hour-${req.ip}`,
20  max: 1000,
21  window: '1h',
22  storage: new MemoryStorage()
23});
24
25// Middleware to check all limiters
26async function rateLimitMiddleware(req, res, next) {
27  const limiters = [
28    { limiter: perSecondLimiter, name: 'per second' },
29    { limiter: perMinuteLimiter, name: 'per minute' },
30    { limiter: perHourLimiter, name: 'per hour' }
31  ];
32
33  for (const { limiter, name } of limiters) {
34    const result = await limiter.check(req);
35    if (!result.allowed) {
36      return res.status(429).json({
37        error: `Rate limit exceeded (${name})`,
38        retryAfter: result.retryAfter
39      });
40    }
41  }
42
43  next();
44}

Custom Error Response

Customize the response when rate limits are exceeded.

custom-error.ts
1import { rateLimitExpress } from 'universal-rate-limiter';
2
3const customRateLimiter = rateLimitExpress({
4  key: (req) => `api-${req.ip}`,
5  max: 100,
6  window: '15m',
7  onRateLimit: (req, res, result) => {
8    res.status(429).json({
9      error: {
10        code: 'RATE_LIMIT_EXCEEDED',
11        message: 'You have exceeded the rate limit',
12        details: {
13          limit: 100,
14          window: '15 minutes',
15          retryAfter: Math.ceil(result.retryAfter / 1000),
16          ip: req.ip
17        }
18      }
19    });
20  }
21});
22
23app.use('/api', customRateLimiter);