npm package: @samahuja642/rate-limiter
Description: A flexible, customizable rate limiting package for Node.js with multiple algorithms and storage backends
Author: Samarth Ahuja
Latest version: 1.0.0

# @samahuja642/rate-limiter

A flexible, production-ready rate limiting package for Node.js with multiple algorithms, storage backends, and framework integrations.

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## Features

- 🚀 **Multiple Algorithms**: Fixed Window, Leaky Bucket
- 💾 **Flexible Storage**: In-memory (default) and Redis support
- 🔌 **Framework Integration**: Express and generic middleware
- 📘 **TypeScript**: Full type definitions included
- ⚡ **High Performance**: Minimal overhead, optimized for production
- 🛡️ **Battle-tested**: Comprehensive test coverage
- 🎯 **Customizable**: Extensive configuration options

## Installation

```bash
npm install @samahuja642/rate-limiter
```

For Redis support:
```bash
npm install @samahuja642/rate-limiter redis
# or
npm install @samahuja642/rate-limiter ioredis
```

## Quick Start

### Express with Fixed Window (In-Memory)

```javascript
const express = require('express');
const { FixedWindow, MemoryStorage, createExpressMiddleware } = require('@samahuja642/rate-limiter');

const app = express();

// Create storage and rate limiter
const storage = new MemoryStorage();
const limiter = new FixedWindow(storage, {
  maxRequests: 100,     // Max requests
  windowMs: 60000       // Per minute
});

// Apply to all routes
app.use(createExpressMiddleware({ limiter }));

app.get('/api/data', (req, res) => {
  res.json({ message: 'Success!' });
});

app.listen(3000);
```

### Express with Redis (Distributed)

```javascript
const express = require('express');
const redis = require('redis');
const { FixedWindow, RedisStorage, createExpressMiddleware } = require('@samahuja642/rate-limiter');

const app = express();

// Create Redis client and storage
const redisClient = redis.createClient({ url: 'redis://localhost:6379' });
await redisClient.connect();

const storage = new RedisStorage(redisClient);
const limiter = new FixedWindow(storage, {
  maxRequests: 100,
  windowMs: 60000 // 100 requests per minute
});

app.use(createExpressMiddleware({ limiter }));

app.listen(3000);
```

## Algorithms

### Comparison Table

| Algorithm | Bursts Allowed | Memory Usage | Accuracy | Best For |
|-----------|---------------|--------------|----------|----------|
| **Fixed Window** | ⚠️ At boundaries | Very Low | Fair | Simple rate limiting |
| **Leaky Bucket** | ❌ No | Low | Good | Traffic shaping |

### Fixed Window

Simple counter that resets at fixed intervals.

```javascript
const limiter = new FixedWindow(storage, {
  maxRequests: 100,
  windowMs: 60000 // 100 requests per minute
});
```

**Use case**: Simple rate limiting, low-memory environments

### Leaky Bucket

Smooths traffic to a constant output rate.

```javascript
const limiter = new LeakyBucket(storage, {
  capacity: 100,
  leakRate: 10 // 10 requests per second
});
```

**Use case**: Upstream rate limiting, traffic shaping

## Storage Backends

### In-Memory Storage

Default storage for single-instance applications.

```javascript
const { MemoryStorage } = require('@samahuja642/rate-limiter');

const storage = new MemoryStorage(300000); // Cleanup every 5 minutes
```

**Pros**: Fast, no external dependencies  
**Cons**: Not suitable for multi-instance deployments

### Redis Storage

For distributed applications across multiple instances.

```javascript
const redis = require('redis');
const { RedisStorage } = require('@samahuja642/rate-limiter');

const client = redis.createClient({ url: 'redis://localhost:6379' });
await client.connect();

const storage = new RedisStorage(client, 'myapp:ratelimit:');
```

**Pros**: Distributed, persistent, scalable  
**Cons**: Requires Redis server

### Custom Storage

Implement the `Storage` interface for custom backends:

```typescript
interface Storage {
  get<T>(key: string): Promise<T | null>;
  set<T>(key: string, value: T, ttl?: number): Promise<void>;
  increment(key: string, amount?: number): Promise<number>;
  delete(key: string): Promise<void>;
}
```

## Framework Integration

### Express

```javascript
const { createExpressMiddleware } = require('@samahuja642/rate-limiter');

app.use(createExpressMiddleware({
  limiter,
  keyGenerator: (req) => req.ip,
  onLimitReached: (req, res) => {
    res.status(429).json({ error: 'Too many requests' });
  },
  skip: (req) => req.path === '/health',
  cost: (req) => req.path.includes('/expensive') ? 5 : 1
}));
```


### Generic/Custom Framework

```javascript
const { createGenericRateLimiter } = require('@samahuja642/rate-limiter');

const rateLimiter = createGenericRateLimiter({
  limiter,
  keyExtractor: (context) => context.userId,
  onSuccess: (context, result) => {
    context.setHeader('X-RateLimit-Remaining', result.remaining);
  },
  onFailure: (context, result) => {
    context.sendError(429, 'Rate limit exceeded');
  }
});

// Use in your custom framework
const allowed = await rateLimiter(context);
```

## Advanced Usage

### Different Limits per Route

```javascript
const authLimiter = new FixedWindow(storage, { maxRequests: 5, windowMs: 60000 });
const apiLimiter = new FixedWindow(storage, { maxRequests: 100, windowMs: 60000 });

app.post('/auth/login', createExpressMiddleware({ limiter: authLimiter }), loginHandler);
app.get('/api/data', createExpressMiddleware({ limiter: apiLimiter }), dataHandler);
```

### User-Based Rate Limiting

```javascript
app.use(createExpressMiddleware({
  limiter,
  keyGenerator: (req) => {
    // Rate limit by authenticated user ID
    return req.user?.id || req.ip;
  }
}));
```

### Tiered Rate Limiting

```javascript
app.use(createExpressMiddleware({
  limiter,
  cost: (req) => {
    // Premium users consume fewer tokens
    if (req.user?.tier === 'premium') return 0.5;
    if (req.user?.tier === 'free') return 2;
    return 1;
  }
}));
```

### Custom Response Headers

```javascript
app.use(createExpressMiddleware({
  limiter,
  onLimitReached: (req, res) => {
    res.status(429)
      .set('X-Custom-Header', 'Rate limited')
      .json({
        error: 'Too Many Requests',
        retryAfter: res.getHeader('Retry-After'),
        message: 'Please slow down your requests'
      });
  }
}));
```

## API Reference

### Rate Limiter Methods

All rate limiters implement:

```typescript
// Consume tokens/requests
consume(key: string, tokens?: number): Promise<RateLimitResult>

// Get status without consuming
getStatus(key: string): Promise<RateLimitResult>

// Reset limit for a key
reset(key: string): Promise<void>
```

### RateLimitResult

```typescript
interface RateLimitResult {
  allowed: boolean;      // Whether request is allowed
  remaining: number;     // Tokens/requests remaining
  limit: number;         // Maximum limit
  resetTime: number;     // Unix timestamp when limit resets
  retryAfter?: number;   // Seconds to wait (when allowed is false)
}
```

### HTTP Headers

Standard rate limit headers are automatically set:

- `X-RateLimit-Limit`: Maximum requests allowed
- `X-RateLimit-Remaining`: Requests remaining in current window
- `X-RateLimit-Reset`: Unix timestamp when limit resets
- `Retry-After`: Seconds to wait before retrying (when limit exceeded)

## Testing

```bash
# Run all tests
npm test

# Run with coverage
npm run test:coverage

# Watch mode
npm run test:watch
```

## Best Practices

1. **Choose the Right Algorithm**
   - Use Fixed Window for simple cases
   - Use Leaky Bucket for traffic shaping

2. **Use Redis for Production**
   - In-memory storage doesn't work across multiple instances
   - Redis provides persistence and distribution

3. **Set Appropriate Limits**
   - Start conservative, increase based on monitoring
   - Different limits for different endpoints
   - Consider user tiers (free vs premium)

4. **Monitor and Adjust**
   - Track rate limit hits
   - Adjust limits based on actual usage patterns
   - Log when limits are exceeded

5. **Fail Open on Errors**
   - The middleware fails open by default (allows requests on errors)
   - Prevents outages due to storage issues

## Examples

See the [`examples/`](./examples) directory for complete working examples:

- [`express-basic.js`](./examples/express-basic.js) - Basic Express integration
- [`express-redis.js`](./examples/express-redis.js) - Express with Redis and multiple algorithms
- [`custom-storage.js`](./examples/custom-storage.js) - Custom storage implementation

## License

MIT © Samarth Ahuja

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## Support

- 📧 Email: your.email@example.com
- 🐛 Issues: [GitHub Issues](https://github.com/samahuja642/rate-limiter-package/issues)
- 📖 Documentation: [GitHub Wiki](https://github.com/samahuja642/rate-limiter-package/wiki)