Rate Limit
The Rate Limit package provides flexible rate limiting for oRPC with multiple storage backend support. It includes adapters for in-memory, Redis, and Upstash, along with middleware and plugin helpers for seamless integration.
Installation
npm install @orpc/experimental-ratelimit@latestyarn add @orpc/experimental-ratelimit@latestpnpm add @orpc/experimental-ratelimit@latestbun add @orpc/experimental-ratelimit@latestdeno add npm:@orpc/experimental-ratelimit@latestAvailable Adapters
Memory Adapter
A simple in-memory rate limiter using a sliding window log algorithm. Ideal for single-instance applications or development.
import { MemoryRatelimiter } from '@orpc/experimental-ratelimit/memory'
const limiter = new MemoryRatelimiter({
maxRequests: 10, // Maximum requests allowed
window: 60000, // Time window in milliseconds (60 seconds)
})Redis Adapter
Redis-based rate limiter using atomic Lua scripts for distributed rate limiting.
import { RedisRatelimiter } from '@orpc/experimental-ratelimit/redis'
import { Redis } from 'ioredis'
const redis = new Redis('redis://localhost:6379')
const limiter = new RedisRatelimiter({
eval: async (script, numKeys, ...rest) => {
return redis.eval(script, numKeys, ...rest)
},
maxRequests: 100,
window: 60000,
prefix: 'orpc:ratelimit:', // Optional key prefix
})INFO
You can use any Redis client that supports Lua script evaluation by providing an eval function.
Upstash Adapter
Adapter for @upstash/ratelimit, optimized for serverless environments like Vercel Edge and Cloudflare Workers.
import { Ratelimit } from '@upstash/ratelimit'
import { Redis } from '@upstash/redis'
import { UpstashRatelimiter } from '@orpc/experimental-ratelimit/upstash-ratelimit'
const redis = Redis.fromEnv()
const ratelimit = new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(10, '60 s'),
prefix: 'my-app:',
})
const limiter = new UpstashRatelimiter(ratelimit)Edge Runtime Support
For Edge runtime like Vercel Edge or Cloudflare Workers, pass the waitUntil function to better handle background tasks:
const limiter = new UpstashRatelimiter(ratelimit, {
waitUntil: ctx.waitUntil.bind(ctx),
})Blocking Mode
Some adapters support blocking mode, which waits for the rate limit to reset instead of immediately rejecting requests.
const limiter = new MemoryRatelimiter({
maxRequests: 10,
window: 60000,
blockingUntilReady: {
enabled: true,
timeout: 5000, // Wait up to 5 seconds
},
})Manual Usage
You can use adapters directly without middleware for custom rate limiting logic:
import { MemoryRatelimiter } from '@orpc/experimental-ratelimit/memory'
import { ORPCError } from '@orpc/server'
const limiter = new MemoryRatelimiter({
maxRequests: 5,
window: 60000,
})
const result = await limiter.limit('user:123')
if (!result.success) {
throw new ORPCError('TOO_MANY_REQUESTS', {
data: {
limit: result.limit,
remaining: result.remaining,
reset: result.reset,
},
})
}createRatelimitMiddleware
The createRatelimitMiddleware helper creates middleware for oRPC procedures to enforce rate limits.
import { call, os } from '@orpc/server'
import { MemoryRatelimiter } from '@orpc/experimental-ratelimit/memory'
import { createRatelimitMiddleware, Ratelimiter } from '@orpc/experimental-ratelimit'
import { z } from 'zod'
const loginProcedure = os
.$context<{ ratelimiter: Ratelimiter }>()
.input(z.object({ email: z.email() }))
.use(
createRatelimitMiddleware({
limiter: ({ context }) => context.ratelimiter,
key: ({ context }, input) => `login:${input.email}`,
}),
)
.handler(({ input }) => {
return { success: true }
})
const ratelimiter = new MemoryRatelimiter({
maxRequests: 10,
window: 60000,
})
const result = await call(
loginProcedure,
{ email: '[email protected]' },
{ context: { ratelimiter } }
)Automatic Deduplication
The createRatelimitMiddleware automatically deduplicates rate limit checks when the same limiter and key combination is used multiple times in a request chain. This behavior follows the Dedupe Middleware Best Practice. To disable deduplication, set the dedupe: false option.
Conditional Limiter
You can dynamically choose different limiters based on context:
const premiumLimiter = new MemoryRatelimiter({
maxRequests: 100,
window: 60000,
})
const standardLimiter = new MemoryRatelimiter({
maxRequests: 10,
window: 60000,
})
const result = await call(
loginProcedure,
{ email: '[email protected]' },
{
context: {
ratelimiter: isPremiumUser ? premiumLimiter : standardLimiter,
},
},
)Handler Plugin
The RatelimitHandlerPlugin automatically adds HTTP rate-limiting headers (RateLimit-* and Retry-After) to responses when used with middleware created by createRatelimitMiddleware.
import { RatelimitHandlerPlugin } from '@orpc/experimental-ratelimit'
const handler = new RPCHandler(router, {
plugins: [
new RatelimitHandlerPlugin(),
],
})INFO
The handler can be any supported oRPC handler, such as RPCHandler, OpenAPIHandler, or other custom handlers.
