UNPKG

@unkey/ratelimit

Version:

<div align="center"> <h1 align="center">@unkey/ratelimit</h1> <h5>@unkey/ratelimit is a library for fast global ratelimiting in serverless functions.</h5> </div>

238 lines (230 loc) 7.61 kB
import { RequestOptions } from '@unkey/api/lib/sdks'; import { V2RatelimitGetOverrideRequestBody, V2RatelimitGetOverrideResponseBody, V2RatelimitSetOverrideRequestBody, V2RatelimitSetOverrideResponseBody, V2RatelimitDeleteOverrideRequestBody, V2RatelimitDeleteOverrideResponseBody, V2RatelimitListOverridesRequestBody, V2RatelimitListOverridesResponseBody } from '@unkey/api/models/components'; type Unit = "ms" | "s" | "m" | "h" | "d"; type Duration = `${number} ${Unit}` | `${number}${Unit}`; type Cache = Map<string, RatelimitResponse>; type Limit = { /** * How many requests may pass in the given duration */ limit: number; /** * Either a type string literal or milliseconds */ duration: Duration | number; }; type RatelimitResponse = { /** * Whether the request may pass(true) or exceeded the limit(false) */ success: boolean; /** * Maximum number of requests allowed within a window. */ limit: number; /** * How many requests the user has left within the current window. */ remaining: number; /** * Unix timestamp in milliseconds when the limits are reset. */ reset: number; /** * The override id for the request that was used to override the limit. */ overrideId?: string; }; type LimitOptions = { /** * Expensive requests may use up more resources. You can specify a cost to the request and * we'll deduct this many tokens in the current window. If there are not enough tokens left, * the request is denied. * * @example * * 1. You have a limit of 10 requests per second you already used 4 of them in the current * window. * * 2. Now a new request comes in with a higher cost: * ```ts * const res = await rl.limit("identifier", { cost: 4 }) * ``` * * 3. The request passes and the current limit is now at `8` * * 4. The same request happens again, but would not be rejected, because it would exceed the * limit in the current window: `8 + 4 > 10` * * * @default 1 */ cost?: number; /** * Override the default limit. * * This takes precedence over the limit defined in the constructor as well as any limits defined * for this identifier in Unkey. */ limit?: Limit; }; interface Ratelimiter { limit: (identifier: string, opts?: LimitOptions) => Promise<RatelimitResponse>; } declare class NoopRatelimit implements Ratelimiter { limit(_identifier: string, _opts?: LimitOptions): Promise<RatelimitResponse>; } type RatelimitConfig = Limit & { /** * @default https://api.unkey.com */ baseUrl?: string; /** * The unkey root key. You can create one at https://unkey.dev/app/settings/root-keys * * Make sure the root key has permissions to use ratelimiting. */ rootKey: string; /** * Namespaces allow you to separate different areas of your app and have isolated limits. * * @example tRPC-routes */ namespace: string; /** * Configure a timeout to prevent network issues from blocking your function for too long. * * Disable it by setting `timeout: false` * * @default * ```ts * { * // 5 seconds * ms: 5000, * fallback: { success: false, limit: 0, remaining: 0, reset: Date.now()} * } * ``` */ timeout?: { /** * Time in milliseconds until the response is returned */ ms: number | Duration; /** * A custom response to return when the timeout is reached. * * The important bit is the `success` value, choose whether you want to let requests pass or not. * * @example With a static response * ```ts * { * // 5 seconds * ms: 5000 * fallback: () => ({ success: true, limit: 0, remaining: 0, reset: 0 }) * } * ``` * @example With a dynamic response * ```ts * { * // 5 seconds * ms: 5000 * fallback: (identifier: string) => { * if (someCheck(identifier)) { * return { success: false, limit: 0, remaining: 0, reset: 0 } * } * return { success: true, limit: 0, remaining: 0, reset: 0 } * } * } * ``` */ fallback: RatelimitResponse | ((identifier: string) => RatelimitResponse | Promise<RatelimitResponse>); } | false /** * Configure what happens for unforeseen errors * * @example Letting requests pass * ```ts * onError: () => ({ success: true, limit: 0, remaining: 0, reset: 0 }) * ``` * * @example Rejecting the request * ```ts * onError: () => ({ success: true, limit: 0, remaining: 0, reset: 0 }) * ``` * * @example Dynamic response * ```ts * onError: (error, identifier) => { * if (someCheck(error, identifier)) { * return { success: false, limit: 0, remaining: 0, reset: 0 } * } * return { success: true, limit: 0, remaining: 0, reset: 0 } * } * ``` */; onError?: (err: Error, identifier: string) => RatelimitResponse | Promise<RatelimitResponse> /** * Cache abusive identifiers and block them immediately without a network request. * * ```ts * // in global scope * const cache = new Map() * * const unkey = new Ratelimit({ * // ... * cache: cache, * }) * ```` */; cache?: Cache; /** * * By default telemetry data is enabled, and sends: * runtime (Node.js / Edge) * platform (Node.js / Vercel / AWS) * SDK version */ disableTelemetry?: boolean; }; declare class Ratelimit implements Ratelimiter { private readonly config; private readonly unkey; private readonly cache; constructor(config: RatelimitConfig); /** * Limit a specific identifier, you can override a lot of things about this specific request using * the 2nd argument. * * @example * ```ts * const identifier = getIpAddress() // or userId or anything else * const res = await unkey.limit(identifier) * * if (!res.success){ * // reject request * } * // handle request * ``` */ limit(identifier: string, opts?: LimitOptions): Promise<RatelimitResponse>; private _limit; } type OverrideConfig = { /** * @default https://api.unkey.com */ baseUrl?: string; /** * The unkey root key. You can create one at https://app.unkey.com/settings/root-keys * * Make sure the root key has permissions to use overrides. */ rootKey: string; }; declare class Overrides { private readonly unkey; constructor(config: OverrideConfig); getOverride(request: V2RatelimitGetOverrideRequestBody, options?: RequestOptions): Promise<V2RatelimitGetOverrideResponseBody>; setOverride(request: V2RatelimitSetOverrideRequestBody, options?: RequestOptions): Promise<V2RatelimitSetOverrideResponseBody>; deleteOverride(request: V2RatelimitDeleteOverrideRequestBody, options?: RequestOptions): Promise<V2RatelimitDeleteOverrideResponseBody>; listOverrides(request: V2RatelimitListOverridesRequestBody, options?: RequestOptions): Promise<V2RatelimitListOverridesResponseBody>; } export { type Cache, type Duration, type Limit, type LimitOptions, NoopRatelimit, type OverrideConfig, Overrides, Ratelimit, type RatelimitConfig, type RatelimitResponse, type Ratelimiter };