Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions skills/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
name: ratelimit-ts
description: Lightweight guidance for using the Redis Rate Limit TypeScript SDK, including setup steps, basic usage, and pointers to advanced algorithm, features, pricing, and traffic‑protection docs.
---

# Rate Limit TS SDK

## Quick Start
- Install the SDK and connect to Redis.
- Create a rate limiter and apply it to incoming operations.

Example:
```ts
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";

const redis = new Redis({ url: "<url>", token: "<token>" });
const limiter = new Ratelimit({ redis, limiter: Ratelimit.slidingWindow(5, "10s") });

const { success } = await limiter.limit("user-id");
if (!success) {
// throttled
}
```

## Other Skill Files
- **algorithms.md**: Describes all available rate‑limiting algorithms and how they behave.
- **pricing-cost.md**: Explains pricing, Redis cost implications, and operational considerations.
- **features.md**: Lists SDK features such as prefixes, custom keys, and behavioral options.
- **methods-getting-started.md**: Full method reference for the SDK's API and getting started guide.
- **traffic-protection.md**: Guidance on applying rate limiting for traffic shaping, abuse prevention, and protection patterns.
84 changes: 84 additions & 0 deletions skills/algorithms.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Ratelimiting Algorithms

This documentation explains the three algorithms supported by the ratelimit‑ts SDK: Fixed Window, Sliding Window, and Token Bucket. It focuses on practical usage, pitfalls, and choosing the right algorithm.

## Fixed Window

Divides time into fixed periods (for example, 10‑second windows). Requests increment a counter for the current window and are rejected once the limit is exceeded.

**Pitfalls**
- Burst leakage: many requests at the boundary may bypass intended behavior.
- Stampedes: large client populations may all retry at the start of a window.
- Reset time is based on fixed boundaries, not on the first request.

**When to use**
- When performance and low computational cost are important.
- When small inaccuracies at boundaries are acceptable.

**Example**
```ts
// 10 requests per 10 seconds
const regional = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.fixedWindow(10, "10 s"),
});

const multi = new MultiRegionRatelimit({
redis: [new Redis({/* auth */}), new Redis({/* auth */})],
limiter: MultiRegionRatelimit.fixedWindow(10, "10 s"),
});
```

## Sliding Window

Uses rolling time windows to smooth boundary behavior. Counts requests in the previous window proportionally based on elapsed time.

**Pitfalls**
- Slightly more expensive to compute and approximate.
- Assumes uniform distribution of past requests.
- In multi‑region mode, generates many Redis commands and can slow down operations.
- Reset time exposed via `limit` and `getRemaining` is only the start of the next full window.

**When to use**
- When smoother behavior around window boundaries is important.
- Avoid in multi‑region setups if command count is a concern.

**Example**
```ts
// 10 requests per 10 seconds
const regional = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, "10 s"),
});

// Multi-region is possible but inefficient
const multi = new MultiRegionRatelimit({
redis: [new Redis({/* auth */}), new Redis({/* auth */})],
limiter: MultiRegionRatelimit.slidingWindow(10, "10 s"),
});
```

## Token Bucket

Maintains a bucket of tokens that refill at a defined rate. Each request consumes one token; if none remain, requests are rejected.

**Advantages**
- Smooths bursts naturally.
- Allows high initial burst capacity (`maxTokens > refillRate`).

**Pitfalls**
- Higher computational cost.
- Not yet supported for multi‑region.

**When to use**
- When smoothing request traffic and allowing controlled bursts is important.

**Example**
```ts
// Bucket with max 10 tokens, refilling 5 tokens every 10s
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.tokenBucket(5, "10 s", 10),
analytics: true,
});
```
117 changes: 117 additions & 0 deletions skills/features.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Features

This Skill documents the core features of the Upstash Rate Limiter for TypeScript. It highlights how to apply caching, timeouts, analytics, multiple limit strategies, dynamic limits, and multi-region setups.

## Caching

Caching prevents unnecessary Redis calls when identifiers are already blocked.

Key points:
- Use an in-memory `Map<string, number>` as `ephemeralCache`.
- Default: a new `Map()` is created automatically.
- Disable by setting `ephemeralCache: false`.
- Works only when the cache or rate limiter is created outside serverless handlers.
- Responses blocked by cache return `reason: cacheBlock`.

Example:
```ts
const cache = new Map();
const ratelimit = new Ratelimit({
limiter: Ratelimit.slidingWindow(10, "10 s"),
ephemeralCache: cache,
});
```

## Timeout

A timeout allows requests to proceed if Redis is slow or unreachable.
- Default timeout: 5 seconds
- On timeout success, `reason` reflects this

Example:
```ts
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, "10 s"),
timeout: 1000,
});
```

## Analytics & Dashboard

Analytics collect counts of success/blocked requests.
- Disabled by default; enable via `analytics: true`
- Data is viewable in the Upstash Rate Limit Dashboard
- In edge runtimes, ensure analytics requests complete using `pending` from `limit()`

Example:
```ts
const { pending } = await ratelimit.limit("id");
context.waitUntil(pending);
```

## Using Multiple Limits

Different user tiers can use different limiters.

Example:
```ts
const ratelimit = {
free: new Ratelimit({ prefix: "free", limiter: Ratelimit.slidingWindow(10, "10s") }),
paid: new Ratelimit({ prefix: "paid", limiter: Ratelimit.slidingWindow(60, "10s") }),
};

await ratelimit.free.limit(ip);
await ratelimit.paid.limit(userId);
```

## Custom Rates

Specify how many tokens to subtract per request using `rate`.

Example:
```ts
await ratelimit.limit("identifier", { rate: batchSize });
```

## Multi Region

Multi-region rate limiting provides lower latency and state replication via CRDTs.
- Uses multiple Redis instances
- Trades strict accuracy for global performance

Example:
```ts
const ratelimit = new MultiRegionRatelimit({
redis: [redisUS, redisEU],
limiter: MultiRegionRatelimit.slidingWindow(10, "10 s"),
});

const { pending } = await ratelimit.limit("id");
context.waitUntil(pending);
```

## Dynamic Limits

Update rate limits at runtime without recreating the limiter.
- Works only for single-region limiters (fixedWindow, slidingWindow, tokenBucket)

Example:
```ts
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, "10s"),
dynamicLimits: true,
});

await ratelimit.setDynamicLimit({ limit: 5 });
const current = await ratelimit.getDynamicLimit();
await ratelimit.setDynamicLimit({ limit: false });
```

## Common Pitfalls

- Forgetting to place the cache outside serverless handlers disables effective caching.
- Not calling `context.waitUntil(pending)` in edge runtimes may cause lost analytics/sync requests.
- Multi-region setups cannot guarantee strict limit enforcement.
- Dynamic limits do not work with multi-region limiters.
77 changes: 77 additions & 0 deletions skills/methods-getting-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Upstash Ratelimit Methods (TypeScript)

This document provides a focused, practical reference for all Ratelimit methods. Each section includes direct examples, usage patterns, and common pitfalls.

## limit
Primary method for checking and consuming tokens.

```ts
const { success, remaining, reset, reason, pending } = await ratelimit.limit(
identifier,
{
rate: 2, // optional: consume N tokens
ip: req.ip, // optional: used for deny‑list checks
userAgent: ua, // optional
country: geo?.country,
}
);

if (!success) return "blocked";

// In Cloudflare/Vercel Edge, flush async work
context.waitUntil(pending);
```

Notes:
- `rate` lets a request consume more than 1 token.
- `reason` can be: `timeout`, `cacheBlock`, `denyList`, or undefined.
- When analytics or MultiRegion is enabled, **always handle `pending`** in serverless environments.

## blockUntilReady
Waits for a request to become allowed instead of rejecting immediately.

```ts
const { success } = await ratelimit.blockUntilReady("id", 30_000);
if (!success) return "still blocked after timeout";
```

## resetUsedTokens
Clears the state for an identifier.

```ts
await ratelimit.resetUsedTokens("user123");
```

Useful when granting temporary resets or admin overrides.

## getRemaining
Read-only view of remaining quota.

```ts
const { remaining, reset } = await ratelimit.getRemaining("user123");
```

Common use cases:
- Dashboard queries
- Showing users their remaining quota

## setDynamicLimit
Overrides the global limit at runtime.

```ts
await ratelimit.setDynamicLimit({ limit: 5 }); // set
await ratelimit.setDynamicLimit({ limit: false }); // remove
```

Notes:
- Requires `dynamicLimits: true` in constructor.
- Applies to all future rate checks.

## getDynamicLimit
Fetch the currently active dynamic limit.

```ts
const { dynamicLimit } = await ratelimit.getDynamicLimit();
```

Returns `null` when no override is active.
Loading
Loading