Skip to content

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

sh
npm install @orpc/experimental-ratelimit@latest
sh
yarn add @orpc/experimental-ratelimit@latest
sh
pnpm add @orpc/experimental-ratelimit@latest
sh
bun add @orpc/experimental-ratelimit@latest
sh
deno add npm:@orpc/experimental-ratelimit@latest

Available Adapters

Memory Adapter

A simple in-memory rate limiter using a sliding window log algorithm. Ideal for single-instance applications or development.

ts
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.

ts
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.

ts
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:

ts
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.

ts
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:

ts
import {  } from '@orpc/experimental-ratelimit/memory'
import {  } from '@orpc/server'

const  = new ({
  : 5,
  : 60000,
})

const  = await .('user:123')

if (!.) {
  throw new ('TOO_MANY_REQUESTS', {
    : {
      : .,
      : .,
      : .,
    },
  })
}

createRatelimitMiddleware

The createRatelimitMiddleware helper creates middleware for oRPC procedures to enforce rate limits.

ts
import { ,  } from '@orpc/server'
import {  } from '@orpc/experimental-ratelimit/memory'
import { , Ratelimiter } from '@orpc/experimental-ratelimit'
import {  } from 'zod'

const  = 
  .<{ : Ratelimiter }>()
  .(.({ : .() }))
  .(
    ({
      : ({  }) => .,
      : ({  }, ) => `login:${.}`,
    }),
  )
  .(({  }) => {
    return { : true }
  })

const  = new ({
  : 10,
  : 60000,
})

const  = await (
  ,
  { : 'user@example.com' },
  { : {  } }
)

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:

ts
const premiumLimiter = new MemoryRatelimiter({
  maxRequests: 100,
  window: 60000,
})

const standardLimiter = new MemoryRatelimiter({
  maxRequests: 10,
  window: 60000,
})

const result = await call(
  loginProcedure,
  { email: 'user@example.com' },
  {
    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.

ts
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.

Released under the MIT License.