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
18 changes: 17 additions & 1 deletion apps/content/docs/helpers/ratelimit.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ const limiter = new RedisRatelimiter({
You can use any Redis client that supports Lua script evaluation by providing an `eval` function.
:::

### Upstash Adapter
### Upstash Ratelimit Adapter

Adapter for [@upstash/ratelimit](https://www.npmjs.com/package/@upstash/ratelimit), optimized for serverless environments like Vercel Edge and Cloudflare Workers.

Expand Down Expand Up @@ -103,6 +103,22 @@ const limiter = new UpstashRatelimiter(ratelimit, {

:::

### Cloudflare Ratelimit Adapter

Adapter for [Cloudflare Workers Ratelimit](https://developers.cloudflare.com/workers/runtime-apis/bindings/rate-limit/).

```ts
import { CloudflareRatelimiter } from '@orpc/experimental-ratelimit/cloudflare-ratelimit'

export default {
async fetch(request, env) {
const limiter = new CloudflareRatelimiter(env.MY_RATE_LIMITER)

return new Response(`Hello World!`)
Comment thread
dinwwwh marked this conversation as resolved.
}
}
Comment thread
dinwwwh marked this conversation as resolved.
```

## Blocking Mode

Some adapters support blocking mode, which waits for the rate limit to reset instead of immediately rejecting requests.
Expand Down
8 changes: 7 additions & 1 deletion packages/ratelimit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,20 @@
"types": "./dist/adapters/upstash-ratelimit.d.mts",
"import": "./dist/adapters/upstash-ratelimit.mjs",
"default": "./dist/adapters/upstash-ratelimit.mjs"
},
"./cloudflare-ratelimit": {
"types": "./dist/adapters/cloudflare-ratelimit.d.mts",
"import": "./dist/adapters/cloudflare-ratelimit.mjs",
"default": "./dist/adapters/cloudflare-ratelimit.mjs"
}
}
},
"exports": {
".": "./src/index.ts",
"./memory": "./src/adapters/memory.ts",
"./redis": "./src/adapters/redis.ts",
"./upstash-ratelimit": "./src/adapters/upstash-ratelimit.ts"
"./upstash-ratelimit": "./src/adapters/upstash-ratelimit.ts",
"./cloudflare-ratelimit": "./src/adapters/cloudflare-ratelimit.ts"
},
"files": [
"dist"
Expand Down
17 changes: 17 additions & 0 deletions packages/ratelimit/src/adapters/cloudflare-ratelimit.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { CloudflareRatelimiter } from './cloudflare-ratelimit'

it('cloudflareRatelimiter', async () => {
Comment thread
dinwwwh marked this conversation as resolved.
const ratelimit = {
limit: vi.fn(() => Promise.resolve({ success: true })),
}

const ratelimiter = new CloudflareRatelimiter(ratelimit)

const result = ratelimiter.limit('test-key')

expect(ratelimit.limit).toHaveBeenCalledTimes(1)
expect(ratelimit.limit).toHaveBeenCalledWith({ key: 'test-key' })
expect(result).toBe(ratelimit.limit.mock.results[0]!.value)

expect(await result).toEqual({ success: true })
Comment thread
dinwwwh marked this conversation as resolved.
})
13 changes: 13 additions & 0 deletions packages/ratelimit/src/adapters/cloudflare-ratelimit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { Ratelimiter, RatelimiterLimitResult } from '../types'

export class CloudflareRatelimiter implements Ratelimiter {
constructor(
private readonly ratelimit: {
limit: (options: { key: string }) => Promise<RatelimiterLimitResult>
},
) {}

limit(key: string): Promise<RatelimiterLimitResult> {
return this.ratelimit.limit({ key })
}
}
Comment thread
dinwwwh marked this conversation as resolved.
Loading