upstash-ratelimit – skill Lập trình cho agent AI
Thêm giới hạn tần suất (rate limiting) cho API route, middleware và edge function với @upstash/ratelimit: sliding window, fixed window và token bucket, lưu trữ trên Upstash Redis.
Cài skill upstash-ratelimit bằng một lệnh
Chọn công cụ bạn đang dùng, copy lệnh và dán vào terminal. Skill sẽ nằm trong thư mục skills của công cụ đó và sẵn sàng dùng ở phiên làm việc tiếp theo.
Claude Code
mkdir -p ~/.claude/skills && curl -fsSL https://skill.dn1nguoi.com/z/upstash-ratelimit-3c5c9.tgz | tar xz -C ~/.claude/skills
Gọi trong Claude Code: /upstash-ratelimit – rồi mô tả công việc bạn muốn làm.
Codex
mkdir -p ~/.codex/skills && curl -fsSL https://skill.dn1nguoi.com/z/upstash-ratelimit-3c5c9.tgz | tar xz -C ~/.codex/skills
Gọi trong Codex: $upstash-ratelimit – rồi mô tả công việc bạn muốn làm.
Hermes Agent
mkdir -p ~/.hermes/skills && curl -fsSL https://skill.dn1nguoi.com/z/upstash-ratelimit-3c5c9.tgz | tar xz -C ~/.hermes/skills
Gọi trong Hermes Agent: /upstash-ratelimit – rồi mô tả công việc bạn muốn làm.
DeepSeek
mkdir -p ~/.deepcode/skills && curl -fsSL https://skill.dn1nguoi.com/z/upstash-ratelimit-3c5c9.tgz | tar xz -C ~/.deepcode/skills
Gọi trong DeepSeek: /upstash-ratelimit – rồi mô tả công việc bạn muốn làm.
Nội dung SKILL.md
Nội dung SKILL.md gốc bằng tiếng Anh.
Upstash Ratelimit
Overview
@upstash/ratelimit implements distributed rate limiting on top of Upstash
Redis. Because state lives in Redis, every instance of a serverless function
or edge worker shares the same counters, which an in-memory limiter cannot
do. It ships three algorithms (fixed window, sliding window, token bucket),
per-identifier keys, optional in-memory blocking of already-limited
identifiers, and optional analytics.
When to Use This Skill
- Use when the user needs to limit requests per IP, user, API key, or tenant
across multiple serverless instances or regions.
- Use when protecting login, signup, form, webhook, or LLM endpoints from
abuse and returning 429 Too Many Requests.
- Use when choosing between fixed window, sliding window, and token bucket.
- Do not use for client-side retry/backoff against a third-party API's limits;
see api-rate-limit-handler.
- Do not use for a single long-running process with no shared state; an
in-memory limiter is simpler there.
How It Works
Step 1: Install and configure
npm install @upstash/ratelimit @upstash/redisSet UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN in the environment.
Step 2: Create the limiter once, outside the handler
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
export const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, "10 s"), // 10 requests per 10 seconds
prefix: "rl:api",
analytics: true,
});Constructing the limiter at module scope lets the built-in ephemeral cache
short-circuit blocked identifiers without a Redis call.
Step 3: Call limit() with a stable identifier
const { success, limit, remaining, reset, pending } = await ratelimit.limit(userId);success is false when the identifier is over its limit. reset is a Unix
timestamp in milliseconds. pending is a promise for background work
(analytics, multi-region sync); await it or pass it to waitUntil on edge
runtimes so the function is not frozen before it completes.
Examples
Example 1: Next.js middleware returning 429
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
import { NextResponse, type NextRequest } from "next/server";
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(20, "1 m"),
});
export async function middleware(request: NextRequest) {
// Conservative shared bucket until an authenticated principal or a platform-verified
// client address adapter is integrated. Never trust a caller-supplied forwarding header.
const identifier = "anonymous-global";
const { success, limit, remaining, reset, pending } = await ratelimit.limit(identifier);
await pending; // Or pass to the deployment platform’s verified waitUntil API.
if (!success) {
return new NextResponse("Too Many Requests", {
status: 429,
headers: {
"X-RateLimit-Limit": String(limit),
"X-RateLimit-Remaining": String(remaining),
"X-RateLimit-Reset": String(reset),
"Retry-After": String(Math.max(0, Math.ceil((reset - Date.now()) / 1000))),
},
});
}
return NextResponse.next();
}
export const config = { matcher: "/api/:path*" };The shared anonymous bucket protects against spoofed identifiers but shares quota among callers. For per-user limits, use a server-verified session identity; for per-IP limits, use the deployment platform's trusted address API after verifying proxy overwrite and direct-origin restrictions.
Example 2: Token bucket with per-plan limits
const limiters = {
free: new Ratelimit({
redis: Redis.fromEnv(),
prefix: "rl:free",
limiter: Ratelimit.tokenBucket(5, "10 s", 10), // refill 5 per 10 s, burst 10
}),
pro: new Ratelimit({
redis: Redis.fromEnv(),
prefix: "rl:pro",
limiter: Ratelimit.tokenBucket(50, "10 s", 100),
}),
};
const { success } = await limiters[plan].limit(apiKey);Best Practices
- ✅ Use a stable, low-cardinality identifier (user id, API key, tenant) where
possible; fall back to IP only for anonymous traffic.
- ✅ Set a distinct
prefixper endpoint or plan so limits do not collide. - ✅ Return
Retry-AfterandX-RateLimit-*headers with 429 responses. - ✅ Prefer
slidingWindowfor most APIs; usetokenBucketwhen short bursts
are acceptable; use fixedWindow when the lowest Redis cost matters.
- ❌ Don't construct a new
Ratelimitinside the request handler. - ❌ Don't rely on
pendingcompleting on its own in edge runtimes. - ❌ Don't rate limit by
x-forwarded-forwithout validating it is set by
your proxy; clients can spoof it otherwise.
Limitations
- Requires an Upstash Redis database; it does not work with other Redis
servers or without network access.
- Each
limit()call is at least one HTTP round trip to Redis, so it adds
latency to every request it guards.
- Sliding window is an approximation that assumes an even spread of requests
in the previous window; it is not an exact log.
MultiRegionRatelimittrades strict accuracy for lower latency and does
not support the token bucket algorithm.
- If Redis is unreachable, the default
timeout(5 s) lets requests through
(reason: "timeout"); this fails open, not closed.
- This skill does not replace environment-specific validation, testing, or
expert review.
Security & Safety Notes
Use a server-verified plan and identifier; do not send raw API keys as Redis identifiers. Hash a stable key identifier where appropriate, review analytics disclosure/retention and choose an explicit timeout/error policy for security-sensitive endpoints rather than assuming fail-open is acceptable.
- Rate limiting is one layer of abuse protection, not authentication. Pair it
with auth and input validation.
- The Redis token grants full database access; keep it server-side.
- Changing limits in production can lock out legitimate users. Confirm the
numbers with the user before deploying stricter limits.
Common Pitfalls
- Problem: Every request is allowed even after the limit.
Solution: Each identifier must be the same string across requests;
check that the identifier is not undefined or a fresh random value.
- Problem: Analytics are empty on Vercel Edge or Cloudflare Workers.
Solution: Pass pending to waitUntil (ctx.waitUntil(pending)) so
the background request is not cancelled when the response is sent.
Related Skills
@upstash-redis- The client this package uses for storage.@api-rate-limit-handler- Client-side backoff and retry when you are the
one being rate limited.
@upstash-qstash- Queue and smooth traffic to downstream services instead
of rejecting it.
Additional Resources
- [Upstash Ratelimit documentation](https://upstash.com/docs/redis/sdks/ratelimit-ts/overview)
- [@upstash/ratelimit on GitHub](https://github.com/upstash/ratelimit-js)
- [Algorithms guide](https://upstash.com/docs/redis/sdks/ratelimit-ts/algorithms)
Skill liên quan
- us-watchlist-alerts – Danh sách theo dõi cổ phiếu Mỹ miễn phí, cảnh báo giá và bản tin hằng ngày; không cần…
- use-dom – Dùng component DOM của Expo để chạy mã web trong webview trên native và chạy trực tiếp…
- using-git-worktrees – Git worktree tạo không gian làm việc độc lập dùng chung một repository, cho phép làm…
- uv-package-manager – Hướng dẫn toàn diện về uv, trình cài đặt và giải quyết phụ thuộc Python cực nhanh viết…
- varlock – Quản lý biến môi trường an toàn theo mặc định cho các phiên Claude Code.
- varlock-claude-skill – Quản lý biến môi trường an toàn với các kiểm tra rõ ràng nhằm giảm nguy cơ lộ bí mật…
- vercel-ai-sdk-expert – Chuyên gia về Vercel AI SDK. Bao gồm Core API (generateText, streamText), UI hooks…
- vercel-cli-with-tokens – Triển khai và quản lý project trên Vercel bằng xác thực dựa trên token. Dùng khi làm…