OrJDev / trpc-limiter

Open Source Rate Limiter Middleware For tRPC. Supporting Upstash, Memory Store And Much More
135 stars 7 forks source link

Feat: @trpc-limiter/redis #10

Closed OrJDev closed 7 months ago

OrJDev commented 1 year ago

Redis adapter for trpc-limiter.

For now we have an Upstash adapter but it seems like just a vanilla Redis one would be more popular.

I will be working on this on my free time, but feel free to make a pr if I haven't yet.

karrui commented 8 months ago

For those looking for a solution to this, I used rate-limiter-flexible to implement a generic Redis adapter:

import { defineTRPCLimiter } from '@trpc-limiter/core'
import {
  RateLimiterMemory,
  RateLimiterRedis,
  RateLimiterRes,
} from 'rate-limiter-flexible'

import { redis } from '../redis'

// Default 5 queries per second fallback
const rateLimiterMemory = new RateLimiterMemory({
  points: 5,
  duration: 1,
})

export const createTrpcRateLimiter = defineTRPCLimiter({
  store: (opts) =>
    new RateLimiterRedis({
      storeClient: redis,
      keyPrefix: 'RATE_LIMIT',
      points: opts.max,
      duration: opts.windowMs / 1000, // in seconds
      insuranceLimiter: rateLimiterMemory,
    }),
  async isBlocked(store, fingerprint) {
    try {
      await store.consume(fingerprint)
      return null
    } catch (error) {
      if (error instanceof RateLimiterRes) {
        return Math.round(error.msBeforeNext / 1000) || 1
      }
      // Should not happen with `insuranceLimiter`
      throw error
    }
  },
})
OrJDev commented 7 months ago

For those looking for a solution to this, I used rate-limiter-flexible to implement a generic Redis adapter:

import { defineTRPCLimiter } from '@trpc-limiter/core'
import {
  RateLimiterMemory,
  RateLimiterRedis,
  RateLimiterRes,
} from 'rate-limiter-flexible'

import { redis } from '../redis'

// Default 5 queries per second fallback
const rateLimiterMemory = new RateLimiterMemory({
  points: 5,
  duration: 1,
})

export const createTrpcRateLimiter = defineTRPCLimiter({
  store: (opts) =>
    new RateLimiterRedis({
      storeClient: redis,
      keyPrefix: 'RATE_LIMIT',
      points: opts.max,
      duration: opts.windowMs / 1000, // in seconds
      insuranceLimiter: rateLimiterMemory,
    }),
  async isBlocked(store, fingerprint) {
    try {
      await store.consume(fingerprint)
      return null
    } catch (error) {
      if (error instanceof RateLimiterRes) {
        return Math.round(error.msBeforeNext / 1000) || 1
      }
      // Should not happen with `insuranceLimiter`
      throw error
    }
  },
})

thank you for this, i used it to implement https://github.com/OrJDev/trpc-limiter/tree/main/packages/redis