@beignet/professor-rate-limit-upstash (英语)
运行时间 : Beignet需要Node.js 22.12或更新. Bun是可选的.
[叹息] Beignet是实验性的α软件. 这个
0.0.x包行为早期 评估,且在框架结算时,API可能在发布之间发生变化.
向上支撑 RateLimitPort beignet 应用程序的提供者.
提供者安装 ctx.ports.rateLimit 使用
升起雷迪丝 财务报告和审定财务报表
@ upstash/ 速率限制。 。 。 .
createUpstashRateLimitProvider(...) 返回稳定
UpstashRateLimitProvider 类型. UpstashRateLimitConfig 说明
经验证的配置; Zod schema 仍为内部.
特征
- 执行标准
RateLimitPort接口. - 使用 Upstash Redis REST API,所以它不方便服务器.
- 支持每个请求带有可配置密钥前缀的动态限制.
- 支持固定窗口和滑动窗口算法通过
UPSTASH_ALGORITHM。 。 。 . - 约束依赖决定,在5秒后默认失败.
- 显示只读的准备状态检查
ctx.ports.upstash。 。 。 . - Emits devetools 事件为允许, 被屏蔽和失败的点击 .
安装
bun add @beignet/provider-rate-limit-upstash @beignet/core @upstash/redis @upstash/ratelimit
配置
设定这些环境变量 :
| 变量 | 需求 | 说明 | 示例 |
|---|---|---|---|
UPSTASH_REDIS_REST_URL | 对 | 您的 Upstash Redis 更新 URL | https://us1-properly-ancient-12345.upstash.io |
UPSTASH_REDIS_REST_TOKEN | 对 | 您的上方Redis REST 标志 | AXXXeyJpZCI6IjEy... |
UPSTASH_PREFIX | 没有 | 速率限制密钥的密钥前缀( 默认 : beignet:ratelimit(中文(简体) ) | myapp:ratelimit |
UPSTASH_ALGORITHM | 没有 | 利率限制算法, fixed-window 或者说 sliding-window (默认 : fixed-window(中文(简体) ) | sliding-window |
UPSTASH_TIMEOUT_MS | 没有 | 最大等待时间 : 1 结束 2147483647 毫秒( 默认值 : 5000(中文(简体) ) | 2500 |
UPSTASH_TIMEOUT_POLICY | 没有 | fail-closed 拒绝超时检查; fail-open 允许它们并记录退化的强制执行(违约: fail-closed(中文(简体) ) | fail-closed |
选择一个算法
fixed-window(默认)是最便宜的选择:每个窗口一个计数器. 这个 由于客户端可以花一整张 在一个窗口后端和下一个窗口开始时再次限制.sliding-window平滑这些边界,以重压上一个 窗口进入当前之一,但每击一次要花费略多一点的Redis工作.
切换算法改变Redis中计数器的键法,所以在飞行中
窗口在更改时有效重置 UPSTASH_ALGORITHM。 。 。 .
获取 Upstash 证书
- 报名到 上图
- 创建一个新的 Redis 数据库
- 导航到数据库细节页面
- 从“ REST API” 部分复制 REST URL 和 REST 令牌
设置
在 Beignet 启动程序中,安装并电线连接端口:
bun beignet provider add rate-limit-upstash
bun install
上方设置 Upstash 环境变量 。 预设扩展了已有的
供应商注册和应用程序端口接线。 添加 createRateLimitHooks<AppContext>()
从 @beignet/core/server 中的现有钩子列表 server/index.ts 改为
执行合同元数据。 保存启动者的认证, 上下文,
其他钩子, 和提供者.
手工提供者注册,导入 createUpstashRateLimitProvider
从此包中添加 createUpstashRateLimitProvider() 给您的提供者
列表中选择一个选项。 跟着 费率限制指南 页:1
服务器补丁和校验步骤.
赤裸裸的 createRateLimitHooks<AppContext>() 呼叫覆盖 global 财务报告和审定财务报表 user
范围限制。 申报合同 rateLimit: { scope: "ip" } 需要一个
明确的服务器级别 trustedProxy.clientIp本地钩
trustedProxy.clientIp, (中文(简体) ), ipSource,或 earlyKey 选项 :
createServer(...) 启动时失败, 否则无法默默指定
所有不明的交通量 在一个合同范围桶。 偏好服务器级别
trustedProxy: { clientIp: "x-forwarded-for-last" } 在信任的倒数后面
代理或 trustedProxy: { clientIp: "cf-connecting-ip" } 用于平台
头. 仅对故意的覆没使用钩-局部选项,或
ipSource: "none" 选择每个合同使用一个未知客户桶.
默认密钥包括合同名称, 因此不相关的合同不消耗
彼此的极限 用户范围失败 AuthUnauthorizedError 除非
路由钩解决一个用户演员。 习惯 key 或者说 earlyKey 拥有完整
buck 键并应当包括路由身份,除非交叉路由聚合
有意为之.
beignet doctor --strict 安装 Upstash 费率限制供应商的检查
登记在 server/providers.ts 还有
UPSTASH_REDIS_REST_URL编号 :UPSTASH_REDIS_REST_TOKEN 存在于应用程序内
示例或配置.
使用 createUpstashRateLimitProvider(...) 如果您要直接通过配置
而不是读 UPSTASH_* (原始内容存档于2018-10-21). Env vars. 选项覆盖 env 衍生值 :
import { createUpstashRateLimitProvider } from "@beignet/provider-rate-limit-upstash";
export const providers = [
createUpstashRateLimitProvider({
redisRestUrl: secrets.upstashRedisRestUrl,
redisRestToken: secrets.upstashRedisRestToken,
prefix: "myapp:ratelimit",
algorithm: "sliding-window",
timeoutMs: 2500,
timeoutPolicy: "fail-closed",
}),
];
通话 createUpstashRateLimitProvider() 没有选项
配置 .
直接适配器
使用 createUpstashRateLimit(...) 当应用程序已经拥有 Upstash Redis 时
客户端 :
import { Redis } from "@upstash/redis";
import { createUpstashRateLimit } from "@beignet/provider-rate-limit-upstash";
const client = Redis.fromEnv();
const rateLimit = createUpstashRateLimit({
client,
prefix: "myapp:ratelimit",
algorithm: "sliding-window",
timeoutMs: 2500,
timeoutPolicy: "fail-closed",
});
直接工厂默认为相同的 beignet:ratelimit 前缀和
fixed-window 算法作为提供者。 它没有读取环境变量
并且不拥有客户生命周期。 过 instrumentation 保留
直接接线的提供者事件.
直接使用
供应商注册后,可以在钩子中使用速率限制端口, 政策,或使用案例:
// Example app-specific policy that rate limits by IP address
async function checkIpRateLimit(ctx: AppContext) {
const result = await ctx.ports.rateLimit.hit({
key: `ip:${ctx.ip}`,
limit: 100,
windowSec: 60, // 100 requests per 60 seconds
});
if (!result.allowed) {
return {
status: 429,
headers: {
"X-RateLimit-Limit": "100",
"X-RateLimit-Remaining": String(result.remaining ?? 0),
"X-RateLimit-Reset": result.resetAt?.toISOString() ?? "",
"Retry-After": String(result.retryAfterSeconds ?? 0),
},
body: {
code: "TOO_MANY_REQUESTS",
message: "Rate limit exceeded. Please try again later.",
},
};
}
// Request is allowed
return undefined;
}
不同终点的不同费率限额
您可以对不同的操作应用不同的费率限制 :
// Strict rate limit for auth endpoints
const loginResult = await ctx.ports.rateLimit.hit({
key: `login:${ctx.ip}`,
limit: 5,
windowSec: 300, // 5 attempts per 5 minutes
});
// More relaxed rate limit for API endpoints
const apiResult = await ctx.ports.rateLimit.hit({
key: `api:user:${userId}`,
limit: 1000,
windowSec: 3600, // 1000 requests per hour
});
使用合同元数据
您可以定义合同的速率限制元数据 :
const getTodos = api.get("/todos")
.meta({
rateLimit: { max: 60, windowSec: 60, scope: "user" },
});
内置 createRateLimitHooks(...) 帮助读取此元数据并应用
限制通过 ctx.ports.rateLimit。如果您的应用程序需要自定义行为,请保留
并直接调用端口:
type RateLimitMetadata = {
rateLimit?: {
max: number;
windowSec: number;
scope?: "global" | "ip" | "user";
};
};
async function rateLimitFromMeta(ctx: AppContext, meta?: RateLimitMetadata) {
if (!meta?.rateLimit) return;
const { max, windowSec, scope = "global" } = meta.rateLimit;
const actorId =
ctx.actor?.type === "user" && ctx.actor.id ? ctx.actor.id : undefined;
const result = await ctx.ports.rateLimit.hit({
key:
scope === "user"
? `user:${actorId ?? "anonymous"}`
: `${scope}:${ctx.ip ?? "global"}`,
limit: max,
windowSec,
});
if (!result.allowed) {
return {
status: 429,
body: {
code: "TOO_MANY_REQUESTS",
message: "Too many requests",
},
};
}
}
费率限制结果
这个 hit 方法返回 a RateLimitResult 改为:
interface RateLimitResult {
allowed: boolean; // true if the hit is within the limit
remaining: number | null; // requests remaining in the window
resetAt: Date | null; // when the window resets
retryAfterSeconds: number | null; // retry delay when the hit is rejected
}
执行详情
- 算法: 用途
Ratelimit.fixedWindow()默认,或Ratelimit.slidingWindow()何时UPSTASH_ALGORITHM=sliding-window - 后端: Upstash Redis REST API(无服务器兼容)
- 按请求配置: 开关 1
Ratelimit实例数(limit, windowSec, algorithm)组合到支持动态限制时无需 重建限制符hit()调用 - 密钥前缀: 避免键相撞的可配置前缀
开发工具
何时 @beignet/devtools 安装在此提供者之前, 费率限制
检查出现在仪表盘的限速监视器下.
提供者的记录 rateLimit.hit 带有密钥、限制、窗口、,
配置前缀、算法、允许/锁定结果、剩余计数、重置
时间,重试后值,以及持续时间。 提供者的失败记录为
rateLimit.hit.failed。故意故障开放超时记录为
rateLimit.hit.degraded,不是一个普通允许的决定.
逃逸舱门
提供者提供标准 rateLimit 端口加号 ctx.ports.upstash
使用原始 Upstash Redis 客户端进行操作 稳定速率限制端口
不建模 :
// Access the Redis client for advanced operations.
await ctx.ports.upstash.client.get("some:key");
await ctx.ports.upstash.client.set("some:key", "value");
// Use the read-only PING check from readiness and connected preflight.
const health = await ctx.ports.upstash.checkHealth();
要获取所贡献端口的正确类型推断, 请扩展您的端口类型
与 UpstashRateLimitProviderPorts数字 :
import type { UpstashRateLimitProviderPorts } from "@beignet/provider-rate-limit-upstash";
type AppPorts = typeof basePorts & UpstashRateLimitProviderPorts;
// Adds rateLimit plus upstash.client and upstash.checkHealth() to AppPorts.
使用马厩 RateLimitPort 用于正常应用行为。 使用原始数据
客户端只有在 Upstash 特定操作是故意的.
失败行为
嵌入式后置提供者在启动时按要求投掷 Upstash 嵌入式 vars 失踪了 网络错误和上层错误的记录和重写不变.
Upstash 的 SDK 无法作出决定时可以返回允许的结果
暂停前 Beignet 默认将这个条件视为错误 :
之后 UPSTASH_TIMEOUT_MS (默认) 5000, (中文(简体) ), fail-closed 记录
rateLimit.hit.failed 并抛出这样受保护的工作,没有
执行。 设定 UPSTASH_TIMEOUT_POLICY=fail-open 只有可用时
比在暴风雨中执法更重要 未打开的超时
返回剩余/重置值未知的允许结果并发射
rateLimit.hit.degraded。 。 。 .
当地测试
用个假的 RateLimitPort 在路线和使用案例测试中。 为了地方发展,
位于 Upstash 开发数据库的点, 或是连接一个应用程序拥有的内存
需要在此提供者之前先限制费率.
部署说明
速率限制键是生产行为的一部分. 设定 UPSTASH_PREFIX 每个应用程序
因此,预览、中继和制作不共享
自动计数器.
提供商使用 Upstash 的 REST 客户端, 并且没有开始工人或坚持
连接, 因此同一适配器在无服务器和长寿命的 HTTP 中工作
运行时间。 已配置的请求范围超时
并准备PING。 添加 ctx.ports.upstash.checkHealth() 给一个应用公司
准备路线; beignet preflight --connect 发现相同的检查
自动.
测试
提供者包括综合测试。 运行方式:
bun test
许可证
麻省理工学院