### Contributing: Development Commands Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Lists essential commands for contributing to the project, including installing dependencies with Bun, running all tests, and starting development databases using Docker Compose. ```bash # Install dependencies bun install # Run tests bun test # Start development databases docker-compose up -d ``` -------------------------------- ### Bash: Installing better-ratelimit Packages Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Provides the necessary commands to install the core library and specific adapter/plugin packages for better-ratelimit using Bun. It includes installing `better-ratelimit-core` for the main functionality, `better-ratelimit-adapter-redis` for Redis support, and `better-ratelimit-plugin-elysia` for Elysia framework integration. ```bash bun add better-ratelimit-core bun add better-ratelimit-adapter-redis bun add better-ratelimit-plugin-elysia ``` -------------------------------- ### Development: Starting Databases with Docker Compose Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Shows the bash command to start necessary development databases like Redis, Dragonfly, Valkey, and ClickHouse using Docker Compose. It also includes a command to run tests specifically for the Redis adapter. ```bash # Start Redis, Dragonfly, Valkey, ClickHouse docker-compose up -d # Test all databases bun test src/adapters/redis/redis.test.ts ``` -------------------------------- ### Development: Setting Up Environment Variables Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Details the process of setting up environment variables for development. It includes copying an example `.env` file and configuring database connection URLs for Redis, Dragonfly, and Valkey. ```bash # Copy example cp env.example .env # Configure databases REDIS_URL=redis://localhost:6379 DRAGONFLY_URL=redis://localhost:6380 VALKEY_URL=redis://localhost:6381 ``` -------------------------------- ### Install better-ratelimit with Bun Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Installs the better-ratelimit package using the Bun package manager. This is the primary method for adding the library to your project. ```bash bun add better-ratelimit ``` -------------------------------- ### Elysia Basic Rate Limiter Setup Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Demonstrates how to integrate the better-ratelimit plugin with Elysia for basic rate limiting. It requires Elysia and the elysia-plugin-better-ratelimit, using Redis as the storage layer. The rate limiter is configured to limit requests by IP address. ```typescript import { Elysia } from "elysia" import { withRateLimiter } from "better-ratelimit-plugin-elysia" import { RedisLayer } from "better-ratelimit-adapter-redis" const app = new Elysia() .use(withRateLimiter({ key: ctx => ctx.ip, limit: 100, duration: "1m", storage: RedisLayer })) .get("/", () => "Hello World!") .listen(3000) ``` -------------------------------- ### TypeScript: MemoryStore Adapter Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Shows how to configure and use the `MemoryStore` adapter for better-ratelimit. This adapter uses in-memory storage, making it suitable for development or low-traffic scenarios where persistence is not required. The example demonstrates instantiating `MemoryStore` with an optional `maxSize` configuration. ```typescript // Memory (default) import { MemoryStore } from "better-ratelimit" const store = new MemoryStore({ maxSize: 1000 }) ``` -------------------------------- ### Framework Integrations - Elysia Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Guides on integrating better-ratelimit with the Elysia framework using the provided plugin. ```APIDOC ## Framework Integrations - Elysia ### Description Integrates better-ratelimit with the Elysia framework, allowing for easy application of rate limiting to routes. ### Method Use the `withRateLimiter` plugin. ### Parameters - **options** (object) - Required - Configuration options for the rate limiter. - **key** (function) - Required - A function to generate the rate limit key (e.g., `ctx => ctx.ip`). - **limit** (number) - Required - The maximum number of requests allowed. - **duration** (string) - Required - The time window for the limit (e.g., '1m', '1h'). - **strategy** (string) - Optional - The rate limiting algorithm ('fixed-window', 'sliding-window', 'approximated-sliding-window'). - **headers** (object) - Optional - Configuration for rate limit response headers. - **enabled** (boolean) - Whether to include rate limit headers. - **prefix** (string) - Prefix for the rate limit headers (e.g., 'X-RateLimit'). - **response** (object) - Optional - Custom response for rate limited requests. - **status** (number) - The HTTP status code (e.g., 429). - **message** (string) - The response body message. - **onLimit** (function) - Optional - A callback function executed when the limit is exceeded. ### Request Example ```typescript import { Elysia } from "elysia" import { withRateLimiter } from "better-ratelimit/dist/plugins/elysia" const app = new Elysia() .use(withRateLimiter({ key: ctx => ctx.ip, limit: 100, duration: "1m", strategy: "fixed-window", headers: { enabled: true, prefix: "X-RateLimit" }, response: { status: 429, message: "Too Many Requests" } })) .get("/api/data", () => ({ data: "..." })) .listen(3000) ``` ``` -------------------------------- ### Effect Advanced Rate Limiter with Observability Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Illustrates advanced usage of better-ratelimit with the Effect library for a more functional and composable approach. It includes Redis storage and Databuddy for observability. The example shows checking rate limits for a specific user key. ```typescript import { Effect, Layer } from "effect" import { RateLimitCore } from "better-ratelimit-core" import { RedisLayer } from "better-ratelimit-adapter-redis" import { DatabuddyLayer } from "better-ratelimit-observability-databuddy" const program = Effect.gen(function* (_) { const rateLimiter = yield* _(RateLimitCore) const result = yield* _( rateLimiter.check({ key: "user:123", limit: 50, duration: "1h" }) ) return result }) const runtime = Layer.provide( Layer.merge(RateLimitCore, RedisLayer), Layer.provide(DatabuddyLayer, program) ) const result = await Effect.runPromise(runtime) ``` -------------------------------- ### TypeScript: RedisAdapter for Rate Limiting Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Demonstrates the configuration and usage of the `RedisAdapter` for better-ratelimit. This adapter allows rate limiting data to be stored in Redis, providing persistence and scalability for production environments. The example shows how to instantiate `RedisAdapter` by providing the Redis connection URL and an optional prefix for the rate limiting keys. ```typescript // Redis import { RedisAdapter } from "better-ratelimit" const store = new RedisAdapter({ url: "redis://localhost:6379", prefix: "ratelimit" }) ``` -------------------------------- ### Helper Methods for Rate Limiter Info Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Illustrates common helper methods for interacting with a rate limiter. It covers checking if a request is allowed (`isAllowed`), retrieving the number of remaining requests (`getRemaining`), getting the reset time (`getResetTime`), and fetching all information at once (`getInfo`). ```typescript import { createRedisRateLimiter } from "better-ratelimit" const limiter = createRedisRateLimiter(process.env.REDIS_URL, 100, "1h") // Simple boolean check if (await limiter.isAllowed("user:123")) { // Process request } // Get remaining requests const remaining = await limiter.getRemaining("user:123") console.log(`${remaining} requests remaining`) // Get reset time const resetTime = await limiter.getResetTime("user:123") console.log(`Resets at ${new Date(resetTime).toISOString()}`) // Get all info at once const info = await limiter.getInfo("user:123") if (!info.allowed) { return { error: "Rate limit exceeded", remaining: info.remaining, retryAfter: new Date(info.resetTime).toISOString() } } ``` -------------------------------- ### Unit Testing Rate Limiter with Bun Test Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Provides an example of how to unit test the rate limiter functionality using Bun's built-in testing framework. It utilizes an in-memory store to simulate request behavior and asserts that requests are correctly allowed or blocked based on the defined limits. ```typescript import { describe, it, expect } from "bun:test" import { RateLimiter, MemoryStore } from "better-ratelimit" describe("Rate Limiting", () => { it("should limit requests correctly", async () => { const limiter = new RateLimiter(new MemoryStore()) // First request - allowed const result1 = await limiter.check({ key: "test:user", limit: 2, duration: "1m" }) expect(result1.allowed).toBe(true) expect(result1.remaining).toBe(1) // Second request - allowed const result2 = await limiter.check({ key: "test:user", limit: 2, duration: "1m" }) expect(result2.allowed).toBe(true) expect(result2.remaining).toBe(0) // Third request - blocked const result3 = await limiter.check({ key: "test:user", limit: 2, duration: "1m" }) expect(result3.allowed).toBe(false) expect(result3.remaining).toBe(0) }) }) ``` -------------------------------- ### TypeScript: Hono Framework Integration with Rate Limiter Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Illustrates integrating better-ratelimit into a Hono application using the `withHonoRateLimiter` plugin. This example configures the rate limiting to use a key derived from the 'x-user-id' header or 'anonymous' if not present. It applies a limit of 100 requests per minute using a sliding window strategy and enables custom rate limit headers. A specific response is defined for when the rate limit is exceeded. ```typescript import { Hono } from "hono" import { withHonoRateLimiter } from "better-ratelimit" const app = new Hono() app.use(withHonoRateLimiter({ key: ctx => ctx.req.header("x-user-id") || "anonymous", limit: 100, duration: "1m", strategy: "sliding-window", headers: { enabled: true, prefix: "X-RateLimit" }, response: { status: 429, message: "Too Many Requests" } })) app.get("/api/data", (c) => c.json({ data: "..." })) ``` -------------------------------- ### Express-Style Middleware for Rate Limiting Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Provides an example of creating an Express.js-style middleware function for rate limiting. This middleware checks the rate limit for a user and responds with a 429 status code if the limit is exceeded, otherwise it proceeds to the next middleware. ```typescript import { createRedisRateLimiter } from "better-ratelimit" const apiLimiter = createRedisRateLimiter(process.env.REDIS_URL, 100, "1h") // Express-style middleware async function rateLimitMiddleware(req: any, res: any, next: any) { const key = `user:${req.userId}` if (!(await apiLimiter.isAllowed(key))) { const info = await apiLimiter.getInfo(key) return res.status(429).json({ error: "Rate limit exceeded", retryAfter: new Date(info.resetTime).toISOString(), remaining: info.remaining }) } next() } ``` -------------------------------- ### TypeScript: Elysia Plugin for Rate Limiter Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Provides an example of integrating the `withRateLimiter` plugin into an Elysia application. This plugin simplifies the process of applying rate limiting middleware to routes. The configuration includes setting the key generator, limit, duration, and strategy, along with an `onLimit` callback for custom handling when a rate limit is exceeded. ```typescript // Elysia import { withRateLimiter } from "better-ratelimit" app.use(withRateLimiter({ key: ctx => ctx.ip, limit: 100, duration: "1m", strategy: "fixed-window", onLimit: (ctx, result) => { // Custom handling } })) ``` -------------------------------- ### File Upload Limiting with Memory Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Implements file upload rate limiting using in-memory storage. It restricts users to uploading 10 files per day. The example includes checking limits, handling exceeded limits, and processing uploads. ```typescript import { createMemoryRateLimiter } from "better-ratelimit" // File upload: 10 files per day per user const uploadLimiter = createMemoryRateLimiter(10, "24h") // In your upload handler async function handleFileUpload(userId: string) { const result = await uploadLimiter.check(`upload:${userId}`) if (!result.allowed) { return { error: "Upload limit reached", remaining: result.remaining, resetDate: new Date(result.resetTime).toLocaleDateString() } } // Process upload... return { success: true, remaining: result.remaining } } ``` -------------------------------- ### In-Memory Storage with LRU Cache (TypeScript) Source: https://context7.com/databuddy-analytics/better-ratelimit/llms.txt Demonstrates the use of `MemoryStore` for in-memory rate limiting. It utilizes an LRU cache with configurable size, TTL, and cleanup intervals. It also supports statistics tracking and event callbacks for entry eviction, setting, and getting. Dependencies include 'better-ratelimit'. Input is a configuration object for the store and rate limiter options, output is the rate limiter's check result and store statistics. ```typescript import { MemoryStore, RateLimiter } from "better-ratelimit" // Create store with custom configuration const store = new MemoryStore({ maxSize: 5000, defaultTTL: 60 * 60 * 1000, // 1 hour cleanupInterval: 5 * 60 * 1000, // 5 minutes enableStats: true, onEvict: (key, value) => { console.log(`Evicted: ${key}, value: ${value.value}`) }, onSet: (key, value) => { console.log(`Set: ${key}, value: ${value.value}, expires: ${value.expires}`) }, onGet: (key, value) => { if (value) { console.log(`Get: ${key}, value: ${value.value}`) } } }) // Create limiter with custom store const limiter = new RateLimiter(store, { limit: 100, duration: "1h", strategy: "fixed-window" }) // Use the limiter await limiter.check("user:123") // Get store statistics const stats = store.getStats() console.log(stats) // { // size: 1, // maxSize: 5000, // options: { ... }, // cleanupInterval: 300000, // defaultTTL: 3600000 // } // Manual cleanup store.cleanup() // Clear all entries store.clear() // Cleanup on shutdown store.destroy() ``` -------------------------------- ### TypeScript: Custom Key Generation with IP Helpers Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Demonstrates using helper functions from better-ratelimit to generate rate limiting keys, specifically focusing on IP address retrieval. The `getIPKey` function is shown to abstract away the complexities of fetching the correct IP address from various proxy and cloud environments (Cloudflare, AWS, Vercel). This allows for more robust and accurate rate limiting based on client IP. ```typescript import { getIPKey } from "better-ratelimit" const result = await limiter.check({ key: getIPKey(ctx), // Handles Cloudflare, AWS, Vercel, etc. limit: 50, duration: "5m" }) ``` -------------------------------- ### Storage Options for Rate Limiter Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Lists available storage adapters for better-ratelimit, including Redis, ClickHouse for analytics, an in-memory store for testing, and BunKV for edge environments. Each option requires importing the corresponding layer. ```typescript // Redis import { RedisLayer } from "better-ratelimit-adapter-redis" // ClickHouse for analytics import { ClickHouseLayer } from "better-ratelimit-adapter-clickhouse" // Memory for testing import { MemoryLayer } from "better-ratelimit-adapter-memory" // BunKV for edge import { BunKVLayer } from "better-ratelimit-adapter-bunkv" ``` -------------------------------- ### Custom Key Generation Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Demonstrates how to generate custom keys for rate limiting, including using helper functions for IP addresses. ```APIDOC ## Custom Key Generation ### Description Shows how to define custom keys for rate limiting, which is essential for granular control. Includes a helper function `getIPKey` for automatically detecting and formatting IP addresses from various proxy setups. ### Method Pass a `key` option with a custom function or use helper functions. ### Parameters - **key** (string | function | object) - The key to use for rate limiting. Can be a string, a function that returns a string, or an object with a `key` property. ### Request Example (Using `getIPKey`) ```typescript import { getIPKey } from "better-ratelimit" // Assuming 'limiter' is an initialized RateLimiter instance // and 'ctx' represents the request context (e.g., from Elysia or Hono) const result = await limiter.check({ key: getIPKey(ctx), // Handles Cloudflare, AWS, Vercel, etc. limit: 50, duration: "5m" }) ``` ### Request Example (Manual Key Generation) ```typescript // In your API handler async function handleApiRequest(requestContext: any) { const userId = requestContext.user.id; // Example: getting user ID from context const apiKey = requestContext.headers['x-api-key']; // Example: getting API key from headers // Combine multiple factors for a unique key const customKey = `user:${userId}:api:${apiKey}`; const result = await limiter.check(customKey); if (!result.allowed) { // Handle rate limit exceeded return { error: "Rate limit exceeded" }; } // Process request... return { success: true }; } ``` ``` -------------------------------- ### RateLimiter Core API Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Demonstrates the core usage of the RateLimiter class, including configuration, checking limits, and accessing helper methods. ```APIDOC ## RateLimiter Core API ### Description Provides the foundational API for creating and managing rate limiters. Allows for configuration of limits, durations, and strategies, with methods for checking limits and retrieving related information. ### Method Instantiate `RateLimiter` ### Endpoint N/A (In-memory class) ### Parameters #### Constructor Parameters - **store** (object) - Required - An instance of a storage adapter (e.g., `MemoryStore`, `RedisAdapter`). - **options** (object) - Required - Configuration options for the rate limiter. - **limit** (number) - Required - The maximum number of requests allowed. - **duration** (string) - Required - The time window for the limit (e.g., '1m', '1h'). - **strategy** (string) - Optional - The rate limiting algorithm ('fixed-window', 'sliding-window', 'approximated-sliding-window'). Defaults to 'fixed-window'. ### Methods - **check(key: string): Promise** - Checks the rate limit for a given key. - **isAllowed(key: string): Promise** - Returns true if the request is allowed, false otherwise. - **getRemaining(key: string): Promise** - Returns the number of remaining requests allowed. - **getInfo(key: string): Promise** - Returns detailed information about the rate limit status for a key. ### Request Example (Initialization) ```typescript import { RateLimiter } from "better-ratelimit" import { MemoryStore } from "better-ratelimit/dist/stores/memory" const store = new MemoryStore() const limiter = new RateLimiter(store, { limit: 100, duration: "1m", strategy: "fixed-window" }) ``` ### Request Example (Checking Limits) ```typescript const result = await limiter.check("user:123") if (await limiter.isAllowed("user:123")) { // Process request } const remaining = await limiter.getRemaining("user:123") const info = await limiter.getInfo("user:123") ``` ### Response #### Success Response (RateLimitResult) - **allowed** (boolean) - Indicates if the request is allowed. - **remaining** (number) - The number of requests remaining in the current window. - **resetTime** (number) - The timestamp when the rate limit resets. - **limit** (number) - The maximum number of requests allowed. - **key** (string) - The identifier used for rate limiting. - **metadata** (object) - Optional - Additional metadata associated with the rate limit. #### Response Example ```json { "allowed": true, "remaining": 99, "resetTime": 1678886400000, "limit": 100, "key": "user:123" } ``` ``` -------------------------------- ### Implement Multi-tier Rate Limiting with Redis Source: https://context7.com/databuddy-analytics/better-ratelimit/llms.txt Shows how to create a multi-tier rate limiting system using Redis, allowing different limits for different user tiers (free, premium, enterprise). It includes a handler function that checks limits, sets custom response headers, and returns appropriate status codes and messages. This is ideal for tiered service offerings. ```typescript import { createRedisRateLimiter } from "better-ratelimit" // Multi-tier rate limiting const tierLimits = { free: createRedisRateLimiter("redis://localhost:6379", 10, "1h", { prefix: "tier:free", strategy: "fixed-window" }), premium: createRedisRateLimiter("redis://localhost:6379", 100, "1h", { prefix: "tier:premium", strategy: "sliding-window" }), enterprise: createRedisRateLimiter("redis://localhost:6379", 10000, "1h", { prefix: "tier:enterprise", strategy: "approximated-sliding-window" }) } // Rate limit handler with tier support async function handleTieredRequest(userId: string, tier: "free" | "premium" | "enterprise") { const limiter = tierLimits[tier] const result = await limiter.check(`user:${userId}`) // Add custom headers for API response const headers = { "X-RateLimit-Limit": result.limit.toString(), "X-RateLimit-Remaining": result.remaining.toString(), "X-RateLimit-Reset": new Date(result.resetTime).toISOString(), "X-RateLimit-Tier": tier } if (!result.allowed) { return { status: 429, error: "Rate limit exceeded", tier, upgradeMessage: tier === "free" ? "Upgrade to premium for higher limits" : undefined, headers } } return { status: 200, data: { message: "Request processed successfully" }, headers } } // Usage examples async function runExamples() { // Free tier user const freeResponse = await handleTieredRequest("user_free_123", "free") console.log("Free tier:", freeResponse) // Premium tier user const premiumResponse = await handleTieredRequest("user_premium_456", "premium") console.log("Premium tier:", premiumResponse) // Enterprise tier user const enterpriseResponse = await handleTieredRequest("user_enterprise_789", "enterprise") console.log("Enterprise tier:", enterpriseResponse) } runExamples() ``` -------------------------------- ### Storage Adapters Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Details on different storage adapters available for better-ratelimit, including MemoryStore and RedisAdapter. ```APIDOC ## Storage Adapters ### Description Allows configuration of different backends for storing rate limiting data. Supports in-memory storage for development and Redis for production. ### MemoryStore #### Description A fast, in-memory storage adapter suitable for development environments. #### Initialization ```typescript import { MemoryStore } from "better-ratelimit/dist/stores/memory" const store = new MemoryStore({ maxSize: 1000 }) ``` ### RedisAdapter #### Description Production-ready storage adapter for Redis, supporting Dragonfly/Valkey. #### Initialization ```typescript import { RedisAdapter } from "better-ratelimit/dist/adapters/redis" const store = new RedisAdapter({ url: "redis://localhost:6379", prefix: "ratelimit" }) ``` ``` -------------------------------- ### API Rate Limiting with Redis Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Demonstrates how to implement API rate limiting using Redis with a sliding window strategy. It limits requests per user to 100 per hour. Includes checking limits, handling exceeded limits, and processing requests. ```typescript import { createRedisRateLimiter } from "better-ratelimit" // API endpoint rate limiting: 100 requests per hour per user const apiLimiter = createRedisRateLimiter( process.env.REDIS_URL, 100, "1h", { prefix: "api:ratelimit", strategy: "sliding-window" } ) // In your API route handler async function handleApiRequest(userId: string) { const result = await apiLimiter.check(`user:${userId}`) if (!result.allowed) { return { error: "Rate limit exceeded", retryAfter: new Date(result.resetTime).toISOString(), remaining: result.remaining } } // Process the request... return { success: true, remaining: result.remaining } } ``` -------------------------------- ### Observability Options for Rate Limiter Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Details the observability options available for better-ratelimit, including automatic logging to Databuddy, console logging, and a placeholder for custom observability solutions. Each option is represented by an importable layer. ```typescript // Auto-log to Databuddy import { DatabuddyLayer } from "better-ratelimit-observability-databuddy" // Console logging import { ConsoleLayer } from "better-ratelimit-observability-console" // Custom observability import { CustomLayer } from "better-ratelimit-observability-custom" ``` -------------------------------- ### Rate Limiting Strategy Configurations Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Presents configuration options for different rate limiting strategies: Token Bucket (default), Sliding Window, and Fixed Window. Each configuration specifies the strategy type, limit, and duration, with Token Bucket also including a burst parameter. ```typescript // Token Bucket (default) { strategy: "token-bucket", limit: 100, duration: "1m", burst: 10 } // Sliding Window { strategy: "sliding-window", limit: 100, duration: "1m" } // Fixed Window { strategy: "fixed-window", limit: 100, duration: "1m" } ``` -------------------------------- ### Implement Fixed, Sliding, and Approximated Sliding Window Rate Limiters Source: https://context7.com/databuddy-analytics/better-ratelimit/llms.txt Demonstrates the creation and usage of different rate limiting strategies: Fixed Window, Sliding Window, and Approximated Sliding Window using memory-based limiters. These are useful for API quotas, preventing abuse, and high-performance scenarios. The code tests each strategy by making multiple requests and logging the results. ```typescript import { createMemoryRateLimiter } from "better-ratelimit" // Fixed Window: Simple, predictable, resets at fixed intervals // Good for: API quotas, simple rate limiting const fixedWindow = createMemoryRateLimiter(100, "1h", { strategy: "fixed-window" }) // Sliding Window: Smooth rate limiting, no burst at window boundaries // Good for: Preventing abuse, traffic shaping const slidingWindow = createMemoryRateLimiter(100, "1h", { strategy: "sliding-window" }) // Approximated Sliding Window: Efficient sliding with sub-windows // Good for: High-performance distributed systems const approximatedSlidingWindow = createMemoryRateLimiter(100, "1h", { strategy: "approximated-sliding-window" }) // Test different strategies async function compareStrategies() { console.log("=== Fixed Window ===") for (let i = 0; i < 3; i++) { const result = await fixedWindow.check("test:fixed") console.log(`Request ${i + 1}: allowed=${result.allowed}, remaining=${result.remaining}`) } console.log("\n=== Sliding Window ===") for (let i = 0; i < 3; i++) { const result = await slidingWindow.check("test:sliding") console.log(`Request ${i + 1}: allowed=${result.allowed}, remaining=${result.remaining}`) } console.log("\n=== Approximated Sliding Window ===") for (let i = 0; i < 3; i++) { const result = await approximatedSlidingWindow.check("test:approx") console.log(`Request ${i + 1}: allowed=${result.allowed}, remaining=${result.remaining}`) } } compareStrategies() ``` -------------------------------- ### TypeScript: Core RateLimiter API Usage Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Illustrates the fundamental usage of the `RateLimiter` class from better-ratelimit. It shows the initialization of a limiter with a specified store, limit, duration, and strategy. The core functionality includes checking rate limits using `limiter.check(key)`, verifying if a request is allowed with `limiter.isAllowed(key)`, retrieving the number of remaining requests with `limiter.getRemaining(key)`, and fetching detailed rate limit information with `limiter.getInfo(key)`. ```typescript import { RateLimiter } from "better-ratelimit" // ✅ Configure once, use many times const limiter = new RateLimiter(store, { limit: 100, duration: "1m", strategy: "fixed-window" }) // ✅ Simple check - just pass the key const result = await limiter.check("user:123") // ✅ Helper methods for common patterns if (await limiter.isAllowed("user:123")) { // Process request } const remaining = await limiter.getRemaining("user:123") const info = await limiter.getInfo("user:123") ``` -------------------------------- ### Login Attempt Protection with Redis Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Shows how to protect against brute-force login attempts using Redis. It enforces a limit of 5 attempts per 15 minutes per IP address with a fixed-window strategy. The code includes checking attempts, handling exceeded limits, and processing login attempts. ```typescript import { createRedisRateLimiter } from "better-ratelimit" // Login protection: 5 attempts per 15 minutes per IP const loginLimiter = createRedisRateLimiter( process.env.REDIS_URL, 5, "15m", { prefix: "login:ratelimit", strategy: "fixed-window" } ) // In your login endpoint async function handleLogin(ip: string, email: string) { const result = await loginLimiter.check(`ip:${ip}`) if (!result.allowed) { return { error: "Too many login attempts", retryAfter: new Date(result.resetTime).toISOString() } } // Attempt login... return { success: true } } ``` -------------------------------- ### Create Redis Rate Limiter using TypeScript Source: https://context7.com/databuddy-analytics/better-ratelimit/llms.txt Creates a production-ready rate limiter backed by Redis for distributed rate limiting. Supports configuration via environment variables or direct Redis URL. Offers various strategies and metadata options. Returns detailed result objects for each check. ```typescript import { createRedisRateLimiter } from "better-ratelimit" // Create Redis-backed limiter: 100 requests per hour const apiLimiter = createRedisRateLimiter( process.env.REDIS_URL || "redis://localhost:6379", 100, "1h", { prefix: "api:ratelimit", strategy: "sliding-window", metadata: { tier: "premium" } } ) // API endpoint handler async function handleApiRequest(userId: string) { const result = await apiLimiter.check(`user:${userId}`) if (!result.allowed) { return { status: 429, error: "Rate limit exceeded", retryAfter: new Date(result.resetTime).toISOString(), remaining: result.remaining, limit: result.limit } } // Process the API request return { status: 200, data: { message: "Success" }, rateLimit: { remaining: result.remaining, limit: result.limit, resetTime: new Date(result.resetTime).toISOString() } } } // Example usage const response = await handleApiRequest("user_456") console.log(response) ``` -------------------------------- ### TypeScript: Elysia Framework Integration with Rate Limiter Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Shows how to integrate the better-ratelimit middleware into an Elysia application. The `withRateLimiter` plugin is used to apply rate limiting to all routes. It configures the key generation based on the request IP, sets a limit of 100 requests per minute, and specifies a fixed-window strategy. Customization includes enabling rate limit headers and defining a custom response for exceeding the limit. ```typescript import { Elysia } from "elysia" import { withRateLimiter } from "better-ratelimit" const app = new Elysia() .use(withRateLimiter({ key: ctx => ctx.ip, limit: 100, duration: "1m", strategy: "fixed-window", headers: { enabled: true, prefix: "X-RateLimit" }, response: { status: 429, message: "Too Many Requests" } })) .get("/api/data", () => ({ data: "..." })) .listen(3000) ``` -------------------------------- ### Redis Rate Limiter Source: https://context7.com/databuddy-analytics/better-ratelimit/llms.txt Creates a production-ready rate limiter backed by Redis, supporting distributed rate limiting across multiple application instances. ```APIDOC ## createRedisRateLimiter ### Description Creates a production-ready rate limiter backed by Redis, supporting distributed rate limiting across multiple application instances. ### Method `createRedisRateLimiter(redisUrl: string, limit: number, duration: string, options?: RedisRateLimiterOptions)` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```typescript import { createRedisRateLimiter } from "better-ratelimit" // Create Redis-backed limiter: 100 requests per hour const apiLimiter = createRedisRateLimiter( process.env.REDIS_URL || "redis://localhost:6379", 100, "1h", { prefix: "api:ratelimit", strategy: "sliding-window", metadata: { tier: "premium" } } ) // API endpoint handler async function handleApiRequest(userId: string) { const result = await apiLimiter.check(`user:${userId}`) if (!result.allowed) { return { status: 429, error: "Rate limit exceeded", retryAfter: new Date(result.resetTime).toISOString(), remaining: result.remaining, limit: result.limit } } // Process the API request return { status: 200, data: { message: "Success" }, rateLimit: { remaining: result.remaining, limit: result.limit, resetTime: new Date(result.resetTime).toISOString() } } } // Example usage const response = await handleApiRequest("user_456") console.log(response) ``` ### Response #### Success Response (200) This method does not directly return an HTTP response. It returns a `RateLimiter` instance. #### Response Example ```json { "allowed": true, "remaining": 99, "limit": 100, "resetTime": 1678890000000, "key": "user:user_456", "metadata": { "tier": "premium" } } ``` ``` -------------------------------- ### Redis Adapter for Distributed Rate Limiting (TypeScript) Source: https://context7.com/databuddy-analytics/better-ratelimit/llms.txt Implements distributed rate limiting using a `RedisAdapter`. This allows multiple application instances to share the same rate limiting counters. It supports automatic key expiration and various rate limiting strategies. Dependencies include 'better-ratelimit' and a Redis client. Input is a Redis connection URL and prefix, output is the rate limiter's check result and potential error information. ```typescript import { RedisAdapter, RateLimiter } from "better-ratelimit" // Create Redis adapter const store = new RedisAdapter({ url: "redis://localhost:6379", prefix: "myapp:ratelimit" }) // Create limiter with Redis backend const limiter = new RateLimiter(store, { limit: 1000, duration: "1h", strategy: "approximated-sliding-window", metadata: { environment: "production" } }) // Distributed rate limiting example async function handleDistributedRequest(userId: string) { // This works across multiple application instances const result = await limiter.check(`user:${userId}`) if (!result.allowed) { return { error: "Rate limit exceeded", resetTime: result.resetTime, retryIn: Math.ceil((result.resetTime - Date.now()) / 1000) } } return { success: true, remaining: result.remaining, limit: result.limit } } // Multiple instances can safely check the same keys const instance1 = await handleDistributedRequest("user_999") const instance2 = await handleDistributedRequest("user_999") console.log(instance1) console.log(instance2) // Close Redis connection when done await store.close() ``` -------------------------------- ### Implement Rate Limiting with Elysia Middleware (TypeScript) Source: https://context7.com/databuddy-analytics/better-ratelimit/llms.txt The `withRateLimiter` middleware integrates seamlessly with the Elysia framework to enforce rate limits on API endpoints. It supports custom key generation, various strategies, configurable response messages, and headers for tracking rate limit status. The `onLimit` and `onSuccess` callbacks allow for custom logic when limits are exceeded or requests are successful. ```typescript import { Elysia } from "elysia" import { withRateLimiter } from "better-ratelimit" const app = new Elysia() .use(withRateLimiter({ key: ctx => ctx.headers["x-api-key"] || ctx.ip, limit: 100, duration: "1m", strategy: "sliding-window", headers: { enabled: true, prefix: "X-RateLimit", includeMetadata: true }, response: { status: 429, message: "Too Many Requests" }, onLimit: (ctx, result) => { console.log(`Rate limit exceeded for ${result.key}`) }, onSuccess: (ctx, result) => { console.log(`Request allowed: ${result.remaining} remaining`) } })) .get("/api/users", () => { return { users: ["Alice", "Bob", "Charlie"] } }) .get("/api/posts", () => { return { posts: ["Post 1", "Post 2"] } }) .listen(3000) console.log(`Server running on http://localhost:3000`) ``` -------------------------------- ### Apply Rate Limiting with Hono Middleware (TypeScript) Source: https://context7.com/databuddy-analytics/better-ratelimit/llms.txt The `withHonoRateLimiter` middleware provides rate limiting capabilities for Hono applications. It allows flexible key generation based on headers or IP addresses and supports different rate limiting strategies. Configurable response status and messages are available, along with options to include rate limit metadata in the response headers. ```typescript import { Hono } from "hono" import { withHonoRateLimiter } from "better-ratelimit" const app = new Hono() // Apply rate limiting middleware app.use("*", withHonoRateLimiter({ key: ctx => { const userId = ctx.req.header("x-user-id") const ip = ctx.req.header("x-forwarded-for") || "unknown" return userId || `ip:${ip}` }, limit: 50, duration: "5m", strategy: "fixed-window", headers: { enabled: true, prefix: "X-RateLimit" }, response: { status: 429, message: "Rate limit exceeded. Please try again later." } })) // Define routes app.get("/api/data", (c) => { return c.json({ data: "Your data here", timestamp: new Date().toISOString() }) }) app.post("/api/submit", async (c) => { const body = await c.req.json() return c.json({ success: true, received: body }) }) export default app ``` -------------------------------- ### Create Memory Rate Limiter using TypeScript Source: https://context7.com/databuddy-analytics/better-ratelimit/llms.txt Creates a rate limiter using in-memory storage with an LRU cache. Ideal for development or single-instance applications. Configurable with limit, duration, strategy, and cache size. Returns result objects with detailed rate limit status. ```typescript import { createMemoryRateLimiter } from "better-ratelimit" // Create limiter: 10 requests per minute const limiter = createMemoryRateLimiter(10, "1m", { strategy: "fixed-window", maxSize: 1000, enableStats: true }) // Check rate limit for a user const result = await limiter.check("user:123") if (!result.allowed) { console.log("Rate limit exceeded") console.log(`Try again at: ${new Date(result.resetTime).toISOString()}`) console.log(`Remaining: ${result.remaining}`) } else { console.log(`Request allowed. ${result.remaining} remaining`) // Process the request } ``` -------------------------------- ### Retrieve Rate Limit Info with getInfo (TypeScript) Source: https://context7.com/databuddy-analytics/better-ratelimit/llms.txt The `getInfo` method retrieves the current rate limit status without affecting the rate limit counter. It's useful for monitoring and displaying rate limit information. This function requires a Redis connection and returns an object containing limit, remaining requests, and reset time. ```typescript import { createRedisRateLimiter } from "better-ratelimit" const limiter = createRedisRateLimiter( "redis://localhost:6379", 100, "1h" ) // Get status endpoint async function getRateLimitStatus(apiKey: string) { const info = await limiter.getInfo(`api:${apiKey}`) return { limit: info.limit, remaining: info.remaining, resetTime: new Date(info.resetTime).toISOString(), percentUsed: ((info.limit - info.remaining) / info.limit * 100).toFixed(1), status: info.remaining > 10 ? "healthy" : "warning" } } // Example usage const status = await getRateLimitStatus("key_abc123") console.log(status) // { // limit: 100, // remaining: 73, // resetTime: "2024-12-28T18:00:00.000Z", // percentUsed: "27.0", // status: "healthy" // } ``` -------------------------------- ### Error Handling Utility Source: https://github.com/databuddy-analytics/better-ratelimit/blob/main/README.md Provides a utility function for creating consistent error responses when rate limits are exceeded. ```APIDOC ## Error Handling Utility ### Description A utility function to standardize error responses when rate limiting is triggered, providing details like retryAfter, remaining, and limit. ### Method `createRateLimitError(result: any): object` ### Parameters - **result** (object) - Required - The result object returned from a rate limiter check. ### Request Example ```typescript import { createRedisRateLimiter } from "better-ratelimit" const limiter = createRedisRateLimiter(process.env.REDIS_URL, 100, "1h") function createRateLimitError(result: any) { return { error: "Rate limit exceeded", retryAfter: new Date(result.resetTime).toISOString(), remaining: result.remaining, limit: result.limit, resetTime: result.resetTime } } async function handleApiRequest(userId: string) { const result = await limiter.check(`user:${userId}`) if (!result.allowed) { return createRateLimitError(result) } // Process request... return { success: true, remaining: result.remaining, resetTime: new Date(result.resetTime).toISOString() } } ``` ### Response Example (Error) ```json { "error": "Rate limit exceeded", "retryAfter": "2023-03-15T10:00:00.000Z", "remaining": 0, "limit": 100, "resetTime": 1678886400000 } ``` ```