### Install sveltekit-rate-limiter with pnpm Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/README.md Install the package as a development dependency using pnpm. ```bash pnpm i -D sveltekit-rate-limiter ``` -------------------------------- ### Install sveltekit-rate-limiter with npm Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/README.md Install the package as a development dependency using npm. ```bash npm i -D sveltekit-rate-limiter ``` -------------------------------- ### Main Entry Point Usage Example Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/export-map.md Demonstrates how to import types from the main package entry point for type checking. ```typescript import type { Rate, RateUnit } from 'sveltekit-rate-limiter'; ``` -------------------------------- ### Cookie Rate Limiter Configuration Example Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/types.md An example demonstrating how to instantiate a RateLimiter with cookie-based options. It shows setting a unique cookie name, a secret from environment variables, rate limits, and specific serialization options like maxAge, secure, and sameSite. ```typescript const limiter = new RateLimiter({ cookie: { name: 'pwd-reset', secret: process.env.COOKIE_SECRET, rate: [1, 'h'], preflight: true, serializeOptions: { maxAge: 60 * 60 * 2, // 2 hours secure: true, // HTTPS only sameSite: 'strict' } } }); ``` -------------------------------- ### Real-World Example: Layered Login Endpoint Protection Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/ip-user-agent-rate-limiter.md This example demonstrates protecting a login endpoint using layered rate limiting with IP, IP+UA, and cookie-based limiters. It shows how to initialize the limiter and check limits before processing login actions. ```typescript import { error } from '@sveltejs/kit'; import { RateLimiter } from 'sveltekit-rate-limiter/server'; const limiter = new RateLimiter({ IP: [100, 'h'], // 100 login attempts per hour per IP IPUA: [20, 'h'], // 20 attempts per hour per browser cookie: { name: 'login-attempt', secret: process.env.COOKIE_SECRET, rate: [5, 'm'], // 5 attempts per minute per session preflight: true } }); export const load = async (event) => { await limiter.cookieLimiter?.preflight(event); return {}; }; export const actions = { login: async (event) => { const status = await limiter.check(event); if (status.limited) { console.log(`Login rate limited by: ${status.reason}`); throw error(429, 'Too many login attempts. Try again later.'); } const data = await event.request.formData(); const email = data.get('email'); const password = data.get('password'); // Authenticate user... } }; ``` -------------------------------- ### IPRateLimiter Hash Method Example Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/ip-rate-limiter.md This example demonstrates how the `hash` method of `IPRateLimiter` extracts the client's IP address from a SvelteKit request event. This method is called internally by the `RateLimiter` class. ```typescript // This is called internally, but conceptually: const event = { getClientAddress: () => '192.0.2.123' }; const limiter = new IPRateLimiter([10, 'h']); const hash = await limiter.hash(event); // hash === '192.0.2.123' ``` -------------------------------- ### Plugin Chain Example Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/README.md Plugins are evaluated in order, sorted by rate window (shortest first). The first plugin to return a clear decision determines the outcome. ```text cookie(2/min) → IPUA(5/min) → IP(10/h) ``` -------------------------------- ### Rate Tuple Examples Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/types.md Examples demonstrating how to define a single rate limit or an array of rate limits using the Rate type. ```typescript const rate: Rate = [10, 'h']; // 10 requests per hour ``` ```typescript const rates: Rate[] = [ [5, 's'], // 5 per second [100, 'm'] // 100 per minute ]; ``` -------------------------------- ### Custom Rate Limiter Plugin Example Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/types.md An example demonstrating how to implement a custom RateLimiterPlugin to allow requests from a specific email domain. This plugin bypasses rate limiting for company emails. ```typescript import type { RateLimiterPlugin } from 'sveltekit-rate-limiter/server'; import type { Rate } from 'sveltekit-rate-limiter/server'; interface CustomPluginData { allowedEmail: string; } class AllowDomainPlugin implements RateLimiterPlugin { readonly rate: Rate = [0, '100ms']; async hash( event: RequestEvent, extraData: CustomPluginData ) { // Return true to allow regardless of rate return extraData.allowedEmail.endsWith('@company.com') ? true : null; } } const limiter = new RateLimiter({ plugins: [new AllowDomainPlugin()], IP: [10, 'h'] }); export const actions = { action: async (event) => { if (await limiter.isLimited(event, { allowedEmail: 'user@company.com' })) { throw error(429); } } }; ``` -------------------------------- ### DatabaseStore Implementation Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/stores.md An example using a SQL database to implement the `RateLimiterStore` interface. This store uses database operations to manage rate limiting counts and expiration. ```APIDOC ## Custom Store Implementation: Database Example using a SQL database: ```typescript import type { RateLimiterStore } from 'sveltekit-rate-limiter/server/stores'; import { db } from './database'; // Your database client export class DatabaseStore implements RateLimiterStore { async add(hash: string, ttl: number): Promise { const now = Date.now(); const expiresAt = now + ttl; // Upsert: increment if exists, insert with count=1 if new const result = await db.rateLimit.upsert({ where: { hash }, create: { hash, count: 1, expiresAt }, update: { count: { increment: 1 }, expiresAt } }); // Clean up expired entries periodically if (Math.random() < 0.01) { await db.rateLimit.deleteMany({ where: { expiresAt: { lt: now } } }); } return result.count; } async clear(): Promise { await db.rateLimit.deleteMany({}); } } // Usage const store = new DatabaseStore(); const limiter = new RateLimiter({ store, IP: [100, 'h'] }); ``` ``` -------------------------------- ### Instantiate RateLimiter with Custom Plugin Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/custom-plugins.md Instantiate the RateLimiter with custom plugins and specific rate limits. This example shows how to integrate the TieredLimiter and set IP-based limits. ```typescript const limiter = new RateLimiter({ plugins: [new TieredLimiter()], IP: [100, 'h'] }); ``` -------------------------------- ### Cloudflare Pages Application Setup Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/cloudflare-limiters.md Example of setting up CloudflareIPUARateLimiter and CloudflareIPRateLimiter for a Cloudflare Pages app. This configuration limits form submissions per browser and per IP address. ```typescript // src/routes/+page.server.ts import { error } from '@sveltejs/kit'; import { RateLimiter } from 'sveltekit-rate-limiter/server'; import { CloudflareIPRateLimiter, CloudflareIPUARateLimiter } from 'sveltekit-rate-limiter/server/limiters'; const limiter = new RateLimiter({ plugins: [ new CloudflareIPUARateLimiter([10, 'm']), // 10 per minute per browser new CloudflareIPRateLimiter([100, 'h']) // 100 per hour per IP ], cookie: { name: 'form-limit', secret: process.env.FORM_SECRET, rate: [3, 'm'], preflight: true } }); export const load = async (event) => { await limiter.cookieLimiter?.preflight(event); }; export const actions = { default: async (event) => { const status = await limiter.check(event); if (status.limited) { throw error(429, `Rate limited by: ${status.reason}`); } const formData = await event.request.formData(); // Process form... } }; ``` -------------------------------- ### CookieRateLimiter Options Example Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/cookie-rate-limiter.md Demonstrates how to instantiate CookieRateLimiter with custom options, including cookie name, secret, rate limit, preflight setting, and serialization options. ```typescript import { RateLimiter } from 'sveltekit-rate-limiter/server'; const limiter = new RateLimiter({ cookie: { name: 'pwd-reset-limiter', secret: process.env.COOKIE_SECRET, rate: [1, 'h'], // 1 request per hour preflight: true, // Require preflight call serializeOptions: { maxAge: 60 * 60 * 2 // Override: 2 hours } } }); ``` -------------------------------- ### Limiter Ordering Example Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/README.md Demonstrates how to configure multiple plugins with different rate windows. The limiter automatically sorts them by window size, shortest first, and stops evaluation early if a limit is reached. ```typescript const limiter = new RateLimiter({ plugins: [ new Plugin1([10, 's']), new Plugin2([100, 'h']) ] }); ``` -------------------------------- ### RedisStore Implementation Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/stores.md An example of a distributed Redis-based store for load-balanced deployments. This store implements the `RateLimiterStore` interface using the Redis client. ```APIDOC ## Custom Store Implementation: Redis Example of a distributed Redis-based store for load-balanced deployments: ```typescript import type { RateLimiterStore } from 'sveltekit-rate-limiter/server/stores'; import { createClient } from 'redis'; export class RedisStore implements RateLimiterStore { private client; constructor(connectionUrl: string) { this.client = createClient({ url: connectionUrl }); } async add(hash: string, ttl: number): Promise { // Increment counter const count = await this.client.incr(hash); // Set TTL (only on first creation) await this.client.expire(hash, Math.ceil(ttl / 1000)); return count; } async clear(): Promise { await this.client.flushDb(); } } // Usage const store = new RedisStore(process.env.REDIS_URL); const limiter = new RateLimiter({ store, IP: [100, 'h'] }); ``` ``` -------------------------------- ### Custom Limiter Example: Return Decisions Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/custom-plugins.md An example implementation of a custom rate limiter plugin that returns specific decisions (allow, reject, or null) based on user ID. ```typescript class CustomLimiter implements RateLimiterPlugin { readonly rate: Rate = [10, 'h']; async hash(event: RequestEvent) { const userId = event.locals.user?.id; if (!userId) { return null; // No user, let next plugin decide } if (userId === 'admin') { return true; // Admins always pass } if (userId === 'banned') { return false; // Banned users always fail } return userId; // Count normal users } } ``` -------------------------------- ### Rate Property Example: Multiple Limits Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/custom-plugins.md An example implementation of a custom rate limiter plugin that defines multiple rate limits using the 'rate' getter. ```typescript class MyPlugin implements RateLimiterPlugin { readonly rate: Rate | Rate[] = [ [1, 's'], // Max 1 per second [100, 'h'] // Max 100 per hour ]; async hash(event: RequestEvent) { return event.getClientAddress(); } } ``` -------------------------------- ### Configure Rate Limiter with Cloudflare Limiters Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/cloudflare-limiters.md Set up a SvelteKit RateLimiter in your hooks.server.ts file using both CloudflareIPUARateLimiter and CloudflareIPRateLimiter. This example demonstrates how to apply different rate limits for different scenarios. ```typescript // src/hooks.server.ts import { RateLimiter } from 'sveltekit-rate-limiter/server'; import { CloudflareIPRateLimiter, CloudflareIPUARateLimiter } from 'sveltekit-rate-limiter/server/limiters'; const limiter = new RateLimiter({ plugins: [ new CloudflareIPUARateLimiter([5, 'm']), new CloudflareIPRateLimiter([100, 'h']) ] }); export const handle: Handle = async ({ event, resolve }) => { if (await limiter.isLimited(event)) { return new Response('Rate limited', { status: 429 }); } return resolve(event); }; ``` -------------------------------- ### Use Short Windows for Early Rejection Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/custom-plugins.md Utilize very short rate windows, like 100ms, for plugins that should execute early. This example shows an AdminCheckPlugin that quickly allows admins before other limits are checked. ```typescript class AdminCheckPlugin implements RateLimiterPlugin { readonly rate: Rate = [0, '100ms']; // Shortest window for early execution async hash(event: RequestEvent) { if (isAdmin(event)) return true; // Quick pass for admins return null; // Let other plugins handle } } ``` -------------------------------- ### RateLimiter Constructor Example Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/types.md Demonstrates how to create a new RateLimiter instance with custom IP, IPUA, and cookie-based rate limits, along with a custom hash function and onLimited callback. Ensure the SECRET environment variable is set for cookie security. ```typescript const limiter = new RateLimiter<{ userId: string }>({ IP: [100, 'h'], IPUA: [20, 'h'], cookie: { name: 'limiter', secret: process.env.SECRET, rate: [5, 'm'], preflight: true }, maxItems: 50000, hashFunction: async (input) => { // Custom hash implementation return someHashFunction(input); }, onLimited: async (event, reason) => { console.log(`Limited: ${reason}`); // Return true to override and allow the request return false; } }); ``` -------------------------------- ### Rate Limiter Call Order Example Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/README.md Illustrates the sequence of limiter calls and request handling for consecutive requests. Shows how limits are applied based on cookie, IPUA, and IP. ```text cookie(2/min) → IPUA(5/min) → IP(10/hour) ``` -------------------------------- ### Server-Only Import Example Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/export-map.md Demonstrates the correct way to import the RateLimiter class in server-side modules and highlights the error that occurs when attempting to import it in a client component. ```typescript // ❌ In a client component import { RateLimiter } from 'sveltekit-rate-limiter/server'; // Error! // ✓ Only in server modules // +page.server.ts, +server.ts, hooks.server.ts, etc. import { RateLimiter } from 'sveltekit-rate-limiter/server'; ``` -------------------------------- ### Custom Plugin Example Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/README.md Create a custom plugin by implementing the RateLimiterPlugin interface. The hash method can use request details like user ID to apply specific rate limits. ```typescript import type { RateLimiterPlugin } from 'sveltekit-rate-limiter/server/limiters'; class UserLimiter implements RateLimiterPlugin { readonly rate = [100, 'h']; async hash(event: RequestEvent) { return event.locals.user?.id || null; } } const limiter = new RateLimiter({ plugins: [new UserLimiter()], IP: [1000, 'h'] }); ``` -------------------------------- ### Basic Rate Limiting Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/export-map.md Initialize a basic rate limiter with IP-based limits. This is the simplest way to start rate limiting requests. ```typescript import { RateLimiter } from 'sveltekit-rate-limiter/server'; const limiter = new RateLimiter({ IP: [10, 'h'] }); ``` -------------------------------- ### RateLimiter Configuration with Various RateUnits Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/types.md Example showing RateLimiter configuration using different time units like 'h' (hour), 'm' (minute), and 's' (second) for various limit types. ```typescript const limiter = new RateLimiter({ IP: [100, 'h'], // Per hour IPUA: [5, 'm'], // Per minute cookie: { name: 'quick', secret: 'key', rate: [1, 's'], // Per second preflight: false } }); ``` -------------------------------- ### Cloudflare Deployment Configuration Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/export-map.md Configure rate limiting for Cloudflare environments using specific Cloudflare plugins. This example shows how to use both `CloudflareIPUARateLimiter` and `CloudflareIPRateLimiter`. ```typescript import { RateLimiter } from 'sveltekit-rate-limiter/server'; import { CloudflareIPRateLimiter, CloudflareIPUARateLimiter } from 'sveltekit-rate-limiter/server/limiters'; const limiter = new RateLimiter({ plugins: [ new CloudflareIPUARateLimiter([10, 'm']), new CloudflareIPRateLimiter([100, 'h']) ] }); ``` -------------------------------- ### RedisStore Implementation of RateLimiterStore Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/types.md An example implementation of the RateLimiterStore interface using Redis. This store uses Redis's incr and expire commands to manage request counts and their Time-To-Live. ```typescript import type { RateLimiterStore } from 'sveltekit-rate-limiter/server'; class RedisStore implements RateLimiterStore { constructor(private redis: any) {} async add(hash: string, ttl: number) { const count = await this.redis.incr(hash); await this.redis.expire(hash, Math.ceil(ttl / 1000)); return count; } async clear() { await this.redis.flushdb(); } } const limiter = new RateLimiter({ store: new RedisStore(redisClient), IP: [100, 'h'] }); ``` -------------------------------- ### Combine Multiple Plugins for Defense-in-Depth Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/custom-plugins.md Layer multiple plugins to create a comprehensive rate limiting strategy. This example shows a combination of geo-blocking, domain whitelisting, API key limiting, and user ID fallback. ```typescript const limiter = new RateLimiter({ plugins: [ new GeoBlockPlugin(), // Block bad regions new AllowedDomainPlugin(), // Whitelist domains new APIKeyLimiter(), // Rate limit by key new UserLimiter() // Fall back to user ID ], IP: [1000, 'h'] // Final IP fallback }); ``` -------------------------------- ### Instantiate Rate Limiter with Custom Data and Plugin Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/README.md Configure the RateLimiter with a custom type parameter for extra data and include the custom domain checking plugin. This setup is used within SvelteKit actions to protect endpoints. ```typescript const limiter = new RateLimiter<{ email: string }>({ plugins: [new AllowDomain('company-domain.com')], IP: [10, 'm'] }); export const actions = { default: async (event) => { if (await limiter.isLimited(event, { email: event.locals.user.email })) { throw error(429); } } }; ``` -------------------------------- ### SQL Database Custom Store Implementation Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/stores.md An example using a SQL database for persistent rate limiting. It employs an upsert operation to increment counts and manages expiration. ```typescript import type { RateLimiterStore } from 'sveltekit-rate-limiter/server/stores'; import { db } from './database'; // Your database client export class DatabaseStore implements RateLimiterStore { async add(hash: string, ttl: number): Promise { const now = Date.now(); const expiresAt = now + ttl; // Upsert: increment if exists, insert with count=1 if new const result = await db.rateLimit.upsert({ where: { hash }, create: { hash, count: 1, expiresAt }, update: { count: { increment: 1 }, expiresAt } }); // Clean up expired entries periodically if (Math.random() < 0.01) { await db.rateLimit.deleteMany({ where: { expiresAt: { lt: now } } }); } return result.count; } async clear(): Promise { await db.rateLimit.deleteMany({}); } } // Usage const store = new DatabaseStore(); const limiter = new RateLimiter({ store, IP: [100, 'h'] }); ``` -------------------------------- ### RedisStore for Load Balanced / Distributed Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/stores.md For load-balanced or distributed setups, use a shared store like Redis. This ensures rate limits are consistent across all servers. ```typescript const store = new RedisStore(process.env.REDIS_URL); const limiter = new RateLimiter({ store, IP: [100, 'h'] }); ``` -------------------------------- ### Implement a Tiered Limiter Plugin Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/custom-plugins.md Implement a custom rate limiter plugin that bypasses limits for premium users. This example demonstrates how to use custom context within the hash method. ```typescript class TieredLimiter implements RateLimiterPlugin { readonly rate: Rate = [10, 'h']; async hash( event: RequestEvent, context: RateLimitContext ): Promise { if (context.isPremium) { return true; // Premium users bypass } return context.userId; } } ``` -------------------------------- ### Integrating Custom Limiter with RateLimiter Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/README.md Demonstrates how to add a custom limiter, such as the IPUserAgentRateLimiter, to the RateLimiter configuration using the 'plugins' option. This example shows adding a custom limiter with a rate of 5 requests per minute. ```typescript import { RateLimiter } from 'sveltekit-rate-limiter/server'; const limiter = new RateLimiter({ plugins: [new CustomLimiter([5, 'm'])] // The built-in limiters can be added as well. }); ``` -------------------------------- ### Configure Rate Limiter with Environment Variables Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/configuration.md Load sensitive configuration values, such as the cookie secret, from environment variables using SvelteKit's `$env/static/private`. This ensures secrets are not hardcoded in the source. The example demonstrates setting up a cookie-based rate limiter. ```typescript import { COOKIE_SECRET } from '$env/static/private'; const limiter = new RateLimiter({ cookie: { name: 'limiter', secret: COOKIE_SECRET, // Load from .env.local rate: [2, 'm'], preflight: true } }); ``` -------------------------------- ### IP + User Agent Rate Limiter Implementation Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/README.md An example implementation of a custom RateLimiterPlugin that limits requests based on a combination of IP address and User-Agent string. It returns false if no User-Agent is present, otherwise it concatenates the IP and UA for hashing. ```typescript import type { RequestEvent } from '@sveltejs/kit'; import type { Rate, RateLimiterPlugin } from 'sveltekit-rate-limiter/server'; class IPUserAgentRateLimiter implements RateLimiterPlugin { readonly rate: Rate | Rate[]; constructor(rate: Rate | Rate[]) { this.rate = rate; } async hash(event: RequestEvent) { const ua = event.request.headers.get('user-agent'); if (!ua) return false; return event.getClientAddress() + ua; } } ``` -------------------------------- ### Dynamic Rate Limits Based on Context Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/custom-plugins.md Create a dynamic limiter that adjusts rate limits based on factors like time of day and server load. This example uses user ID and checks for peak hours and high load. ```typescript import type { RequestEvent } from '@sveltejs/kit'; import type { RateLimiterPlugin } from 'sveltekit-rate-limiter/server'; class DynamicLimiter implements RateLimiterPlugin { async hash(event: RequestEvent): Promise { const userId = event.locals.user?.id; if (!userId) return null; // Different limits based on time of day const hour = new Date().getHours(); const isPeakHours = hour >= 9 && hour <= 17; // Different limits based on load const isHighLoad = await checkServerLoad(); if (isPeakHours && isHighLoad) { // During peak hours under high load: stricter limits return userId; // Will be counted against rate } else { // Off-peak: more lenient return true; // Allow regardless of rate } } get rate(): Rate { return [5, 'm']; } } async function checkServerLoad(): Promise { // Check CPU, memory, or custom metrics return false; } ``` -------------------------------- ### Configuring Rate Limits: Strict vs. Reasonable Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/errors.md Illustrates the difference between overly strict rate limit configurations and more reasonable ones. Use the latter to avoid unnecessary rejections. ```typescript { IP: [1, 's'] } // Only 1 per second ``` ```typescript { IP: [10, 'm'] } // 10 per minute ``` -------------------------------- ### Node.js Crypto HashFunction Example Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/types.md An example of a HashFunction implementation using Node.js's built-in crypto module. This function creates a SHA256 hash of the input string and returns it in hexadecimal format. ```typescript import crypto from 'crypto'; import type { HashFunction } from 'sveltekit-rate-limiter/server'; const nodeHashFunction: HashFunction = (input) => crypto.createHash('sha256').update(input).digest('hex'); const limiter = new RateLimiter({ hashFunction: nodeHashFunction, IP: [10, 'h'] }); ``` -------------------------------- ### IPRateLimiter Constructor and Rate Property Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/ip-rate-limiter.md Demonstrates how to instantiate the IPRateLimiter and access its configured rate limits. ```APIDOC ## IPRateLimiter ### Properties #### rate ```typescript readonly rate: Rate | Rate[]; ``` The configured rate limit(s) as a tuple `[count, unit]` or array of such tuples. Read-only after construction. ### Example ```typescript const limiter = new IPRateLimiter([5, 'm']); console.log(limiter.rate); // [5, 'm'] ``` ``` -------------------------------- ### Configure and use RateLimiter in SvelteKit server files Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/README.md Initialize the RateLimiter with custom rates for IP, IP+User Agent, and cookie-based limiting. Ensure the cookie limiter's preflight is awaited in the load function to prevent direct posting issues. Use isLimited or check methods to enforce rate limits. ```typescript import { error } from '@sveltejs/kit'; import { RateLimiter } from 'sveltekit-rate-limiter/server'; const limiter = new RateLimiter({ // A rate is defined as [number, unit] IP: [10, 'h'], // IP address limiter IPUA: [5, 'm'], // IP + User Agent limiter cookie: { // Cookie limiter name: 'limiterid', // Unique cookie name for this limiter secret: 'SECRETKEY-SERVER-ONLY', // Use $env/static/private rate: [2, 'm'], preflight: true // Require preflight call (see load function) } }); export const load = async (event) => { /** * Preflight prevents direct posting. If preflight option for the * cookie limiter is true and this function isn't called before posting, * request will be limited. * * Remember to await, so the cookie will be set before returning! */ await limiter.cookieLimiter?.preflight(event); }; export const actions = { default: async (event) => { // Every call to isLimited counts as a hit towards the rate limit for the event. if (await limiter.isLimited(event)) throw error(429); }, debug: async (event) => { // Alternatively you can call check to get more details. // (will also count as a hit) const status = await limiter.check(event); if (status.limited) { // 'IP' | 'IPUA' | 'cookie' | number console.log('Limited due to ' + status.reason); throw error(429); } } }; ``` -------------------------------- ### TTLStore with Max Items Limit Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/stores.md To prevent memory growth with TTLStore, set a reasonable `maxItems` limit. This example limits the store to 50,000 entries. ```typescript const store = new TTLStore(3600000, 50000); // Max 50,000 entries ``` -------------------------------- ### SvelteKit Rate Limiter Configuration (Development) Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/cloudflare-limiters.md For development environments without Cloudflare, use the standard `RateLimiter` with IP and IPUA options. No special imports are needed. ```typescript import { RateLimiter } from 'sveltekit-rate-limiter/server'; const limiter = new RateLimiter({ IP: [100, 'h'], IPUA: [20, 'h'] }); ``` -------------------------------- ### Cloudflare cf-connecting-ip Header Example Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/cloudflare-limiters.md Cloudflare automatically adds the `cf-connecting-ip` header to proxied requests, containing the client's real public IP address. ```text cf-connecting-ip: 203.0.113.45 ``` -------------------------------- ### Storage & Extensibility Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/README.md Information on storage implementations and creating custom plugins. ```APIDOC ## Storage Implementations ### Description Details on TTLStore, RetryAfterStore, and custom backends. ### Link [api-reference/stores.md](api-reference/stores.md) ``` ```APIDOC ## Custom Plugins ### Description Guidance on building your own rate limiters. ### Link [api-reference/custom-plugins.md](api-reference/custom-plugins.md) ``` -------------------------------- ### Custom Retry-After Store Implementation Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/retry-after-rate-limiter.md Example of a custom store implementing the RateLimiterStore interface for tracking retry-after timestamps. This store uses a Map to cache timestamps. ```typescript import type { RateLimiterStore } from 'sveltekit-rate-limiter/server'; class CustomRetryStore implements RateLimiterStore { private cache = new Map(); async add(hash: string, ttl: number) { if (this.cache.has(hash)) { return this.cache.get(hash) ?? 0; } const timestamp = Date.now() + ttl; this.cache.set(hash, timestamp); return timestamp; } async clear() { this.cache.clear(); } } const limiter = new RetryAfterRateLimiter( { IP: [10, 'h'] }, new CustomRetryStore() ); ``` -------------------------------- ### Complete Rate Limiter Configuration with Options Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/configuration.md Illustrates a comprehensive configuration including multiple rate definitions, cookie options, custom plugins, storage, and callbacks. ```typescript import { RateLimiter } from 'sveltekit-rate-limiter/server'; import { TTLStore } from 'sveltekit-rate-limiter/server/stores'; import type { RateLimiterOptions } from 'sveltekit-rate-limiter/server'; const options: RateLimiterOptions = { // === Built-in Limiters === // IP-based limiting IP: [10, 'h'], // Single rate // or IP: [ [1, 's'], // Multiple rates [100, 'h'] ], // IP + User-Agent limiting IPUA: [5, 'm'], // Cookie-based limiting cookie: { name: 'limiter-id', secret: process.env.COOKIE_SECRET, // Signing key rate: [2, 'm'], // Rate limit preflight: true, // Require preflight serializeOptions: { // Optional cookie options httpOnly: true, secure: true, sameSite: 'strict', maxAge: 604800 // 7 days }, hashFunction: customHash // Optional custom hash }, // === Custom Configuration === // Custom rate limiter plugins plugins: [ new MyCustomLimiter([5, 'm']), new AnotherLimiter([10, 'h']) ], // Custom storage backend store: new TTLStore(3600000, 50000), // Max items to store (for memory limits) maxItems: 50000, // Custom hash function hashFunction: async (input: string) => { // SHA-256 or other hash implementation return hashValue; }, // Callback when request is rate limited onLimited: async (event, reason) => { console.log(`Limited: ${reason}`); // Return true to override and allow the request return false; } }; const limiter = new RateLimiter(options); ``` -------------------------------- ### Initialize CloudflareIPRateLimiter with Plugin Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/cloudflare-limiters.md Instantiate CloudflareIPRateLimiter as a plugin for the main RateLimiter. Use this when your SvelteKit app is deployed behind Cloudflare to correctly identify client IPs. ```typescript import { RateLimiter } from 'sveltekit-rate-limiter/server'; import { CloudflareIPRateLimiter } from 'sveltekit-rate-limiter/server/limiters'; // Using the plugin directly const limiter = new RateLimiter({ plugins: [new CloudflareIPRateLimiter([10, 'h'])] }); ``` -------------------------------- ### Configure Multiple Cookie Rate Limits Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/cookie-rate-limiter.md Set up multiple rate thresholds for the cookie limiter. Requests exceeding any threshold are rejected, starting with the fastest limit. ```typescript const limiter = new RateLimiter({ cookie: { name: 'api-key', secret: process.env.COOKIE_SECRET, rate: [ [1, 's'], // Max 1 per second [10, 'm'] // Max 10 per minute ], preflight: true } }); ``` -------------------------------- ### Initialize RateLimiter with Multiple Plugins Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/rate-limiter.md Instantiate the RateLimiter with different plugin configurations for IP, IPUA, and cookie-based rate limiting. Plugins are automatically sorted by duration and limit for efficient processing. ```typescript import { RateLimiter } from 'sveltekit-rate-limiter/server'; const limiter = new RateLimiter({ IP: [10, 'h'], // 10 requests per hour by IP IPUA: [5, 'm'], // 5 requests per minute by IP + User Agent cookie: { name: 'limiter-id', secret: 'my-secret-key', rate: [2, 'm'], preflight: true } }); ``` -------------------------------- ### Check Limits with Detailed Status Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/README.md Get a detailed status object indicating whether the event is limited and the reason for the limitation. The 'reason' can be a string identifier or a number for Retry-After. ```typescript const status = await limiter.check(event); // { limited: false } | { limited: true, reason: 'IP' | 'IPUA' | 'cookie' | number } ``` -------------------------------- ### Initialize and Use RetryAfterRateLimiter Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/retry-after-rate-limiter.md Demonstrates how to initialize RetryAfterRateLimiter with custom rate limits and use it within a SvelteKit handle function to check requests and return a 429 response with a Retry-After header. ```typescript import { RetryAfterRateLimiter } from 'sveltekit-rate-limiter/server'; import type { Handle } from '@sveltejs/kit'; const limiter = new RetryAfterRateLimiter({ IP: [10, 'h'], IPUA: [5, 'm'] }); export const handle: Handle = async ({ event, resolve }) => { const status = await limiter.check(event); if (status.limited) { return new Response( `Rate limited. Retry after ${status.retryAfter} seconds.`, { status: 429, headers: { 'Retry-After': status.retryAfter.toString() } } ); } return resolve(event); }; ``` -------------------------------- ### Configuring Multiple Rates Per Limiter Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/README.md Shows how to specify an array of rates to handle multiple limits for a single limiter, such as 1 per second and 100 per hour. ```javascript [[1, 's'], [100, 'h']] ``` -------------------------------- ### Instantiate RetryAfterStore and RetryAfterRateLimiter Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/stores.md Demonstrates how to create an instance of RetryAfterStore with a maximum item limit and then use it to configure a RetryAfterRateLimiter. ```typescript import { RetryAfterStore } from 'sveltekit-rate-limiter/server/stores'; import { RetryAfterRateLimiter } from 'sveltekit-rate-limiter/server'; const retryStore = new RetryAfterStore(50000); const limiter = new RetryAfterRateLimiter( { IP: [10, 'h'] }, retryStore ); ``` -------------------------------- ### Basic Rate Limiter Configuration Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/configuration.md Demonstrates basic configuration using built-in IP, IP+User-Agent, and cookie limiters. ```typescript const limiter = new RateLimiter({ // Option 1: Built-in IP limiter IP: [10, 'h'], // Option 2: Built-in IP + User-Agent limiter IPUA: [5, 'm'], // Option 3: Built-in cookie limiter cookie: { name: 'limiter-id', secret: 'your-secret-key', rate: [2, 'm'], preflight: true } }); ``` -------------------------------- ### Multiple Rate Thresholds Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/ip-rate-limiter.md Illustrates how to set up multiple rate thresholds for burst and sustained limits. ```APIDOC ### Pattern 2: Multiple Rate Thresholds Implement burst and sustained limits: ```typescript const limiter = new RateLimiter({ IP: [ [10, 's'], // Allow bursts: 10 per second [1000, 'h'] // Sustained limit: 1000 per hour ] }); ``` Requests are rejected if they exceed either threshold. ``` -------------------------------- ### Check Rate Limit in Action Handler Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/custom-plugins.md Check if a request is limited within an action handler, passing custom context to the limiter. This example demonstrates how to use the instantiated limiter to protect an action. ```typescript export const actions = { submitForm: async (event) => { const user = await event.locals.user; if (await limiter.isLimited(event, { userId: user.id, isPremium: user.tier === 'premium', customData: user.metadata })) { throw error(429, 'Rate limited'); } } }; ``` -------------------------------- ### Method-Based Limiting Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/custom-plugins.md Implement different rate limits for different HTTP methods (e.g., GET vs. POST) by creating distinct rate buckets based on the request method and IP address. ```typescript class MethodLimiter implements RateLimiterPlugin { readonly rate: Rate | Rate[] = { 'GET': [100, 'h'], 'POST': [10, 'h'] } as any; // Type workaround async hash(event: RequestEvent): Promise { const method = event.request.method; const ip = event.getClientAddress(); // Different rate limit buckets per method return `${method}:${ip}`; } } ``` -------------------------------- ### Combined with Other Limiters Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/ip-rate-limiter.md Demonstrates layering IP limiting with other types of limiters for more granular control. ```APIDOC ### Pattern 3: Combined with Other Limiters Layer IP limiting with more granular controls: ```typescript const limiter = new RateLimiter({ IP: [1000, 'h'], // Broad: 1000 per hour per IP IPUA: [100, 'h'], // Medium: 100 per hour per IP+UA cookie: { name: 'session-limit', secret: process.env.SECRET, rate: [10, 'h'], preflight: false } }); ``` Limiters are evaluated in order of strictness (shortest window first). A request is rejected by whichever limiter it hits first. ``` -------------------------------- ### Instantiate Basic Rate Limiter Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/README.md Use this to create a basic rate limiter instance. Configure limits using an object where keys are identifiers (e.g., 'IP') and values are arrays representing the count and time unit. ```typescript const limiter = new RateLimiter({ IP: [10, 'h'] }); ``` -------------------------------- ### Clear Rate Limit Counters and Retry-After State Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/retry-after-rate-limiter.md Clears all rate limit counters and retry-after timestamps. This is useful for resetting the limiter's state, for example, during application restarts or specific administrative actions. ```typescript // Reset all rate limits and retry-after state await limiter.clear(); ``` -------------------------------- ### SvelteKit Rate Limiter Configuration (Production with Cloudflare) Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/cloudflare-limiters.md In production with Cloudflare, import and use `CloudflareIPRateLimiter` and `CloudflareIPUARateLimiter`. This configuration uses specific rates for Cloudflare and falls back to standard options if not deployed to Cloudflare. ```typescript import { RateLimiter } from 'sveltekit-rate-limiter/server'; import { CloudflareIPRateLimiter, CloudflareIPUARateLimiter } from 'sveltekit-rate-limiter/server/limiters'; const limiters = process.env.DEPLOYED_TO === 'cloudflare' ? [ new CloudflareIPUARateLimiter([5, 'm']), new CloudflareIPRateLimiter([100, 'h']) ] : []; // Use standard IP/IPUA options in other deployments const limiter = new RateLimiter({ plugins: limiters, cookie: { name: 'api-limit', secret: process.env.COOKIE_SECRET, rate: [10, 'h'], preflight: false } }); ``` -------------------------------- ### Detailed Error Messages with check() Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/errors.md Use the `check()` method to get detailed rate limiting status and throw specific error messages based on the reason. This is useful for providing user-friendly feedback during login attempts. ```typescript export const actions = { login: async (event) => { const status = await limiter.check(event); if (status.limited) { const messages = { 'IP': 'Too many login attempts from your location', 'IPUA': 'Too many login attempts from your browser', 'cookie': 'Please wait before trying again', }; const message = messages[status.reason as string] || 'Rate limited. Try again later.'; throw error(429, message); } } }; ``` -------------------------------- ### Initialize TTLStore with RateLimiter Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/stores.md Create an in-memory TTL store for tracking request counts and initialize the RateLimiter with custom rate limits. Ensure maxTTL is set to the largest rate window in use. ```typescript import { TTLStore } from 'sveltekit-rate-limiter/server/stores'; import { TTLTime } from 'sveltekit-rate-limiter/server'; // If the largest rate window is 1 hour (3,600,000 ms) const store = new TTLStore( TTLTime('h'), // 3,600,000 ms for 1 hour 50000 // Max 50,000 rate limit keys in memory ); const limiter = new RateLimiter({ store, IP: [100, 'h'], IPUA: [20, 'h'] }); ``` -------------------------------- ### Configure Plugin Ordering by Rate Window Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/custom-plugins.md Configure the RateLimiter with multiple plugins, demonstrating how they are automatically sorted by their rate window from shortest to longest. This ensures faster limits are checked first. ```typescript const limiter = new RateLimiter({ plugins: [ new Method1Plugin([10, 's']), // Runs first new Method2Plugin([100, 'h']), // Runs second new Method3Plugin([1000, 'd']) // Runs last ] }); ``` -------------------------------- ### RateLimiter Constructor Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/rate-limiter.md Initializes a new RateLimiter instance with optional configuration. The constructor sets up various rate limiting plugins (IP, IPUA, cookie, custom) and a store for tracking request rates, optimizing for early rejection. ```APIDOC ## Constructor ```typescript constructor(options?: RateLimiterOptions) ``` ### Description Creates a new `RateLimiter` instance. The constructor initializes the rate limiter with configured plugins (IP, IPUA, cookie, or custom) and a store for tracking request rates. Plugins are automatically sorted by rate duration and limit to enable early rejection. ### Parameters - **options** (`RateLimiterOptions`) - Optional - Configuration object for the rate limiter. Defaults to `{}`. ### Example ```typescript import { RateLimiter } from 'sveltekit-rate-limiter/server'; const limiter = new RateLimiter({ IP: [10, 'h'], // 10 requests per hour by IP IPUA: [5, 'm'], // 5 requests per minute by IP + User Agent cookie: { name: 'limiter-id', secret: 'my-secret-key', rate: [2, 'm'], preflight: true } }); ``` ``` -------------------------------- ### Instantiate CloudflareIPUARateLimiter Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/cloudflare-limiters.md Create a new CloudflareIPUARateLimiter instance with a specified rate limit. This is useful for setting up granular rate limiting based on Cloudflare's real IP and the client's User-Agent. ```typescript import { RateLimiter } from 'sveltekit-rate-limiter/server'; import { CloudflareIPUARateLimiter } from 'sveltekit-rate-limiter/server/limiters'; const limiter = new RateLimiter({ plugins: [new CloudflareIPUARateLimiter([10, 'm'])] }); ``` -------------------------------- ### Check Rate Limit and Get Reason Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/rate-limiter.md Utilize the `check` method to determine if a request is rate limited and identify the specific reason (e.g., 'IP', 'IPUA', 'cookie', or custom plugin index). This provides more detailed feedback for handling rate-limited requests. ```typescript export const actions = { default: async (event) => { const status = await limiter.check(event); if (status.limited) { console.log(`Limited by: ${status.reason}`); throw error(429, `Rate limited by ${status.reason}`); } } }; ``` -------------------------------- ### Testing Cloudflare IP Detection Locally Source: https://github.com/ciscoheat/sveltekit-rate-limiter/blob/main/_autodocs/api-reference/cloudflare-limiters.md Demonstrates how to simulate the 'cf-connecting-ip' header for local testing. This allows verification of Cloudflare IP detection logic without a live Cloudflare deployment. ```typescript // Test request with Cloudflare header const response = await fetch('/api/endpoint', { headers: { 'cf-connecting-ip': '203.0.113.45' } }); ```