JSPM

@ambergristle/hono-rate-limiter

0.0.9
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 16
  • Score
    100M100P100Q94766F
  • License MIT

Basic rate limiting for Hono, featuring a variety of algorithms

Package Exports

  • @ambergristle/hono-rate-limiter
  • @ambergristle/hono-rate-limiter/algorithms

Readme

TL;DR

A flexible rate limiting solution designed for Hono + (Upstash) Redis (for now), with four built-in algorithms.

Modularity and interoperability are key goals. The RateLimiter can be used without the middleware, if you prefer to configure request handling yourself. I'll extend support to node-redis soon, and I'll look into implementing the algorithms using Cloudflare KV.

The middleware was inspired by hono-rate-limiter, and the algorithms are borrowed from [`@upstash/ratelimit-js](github.com/upstash/ratelimit-js) and Lucia Auth.

Get Started

import { Redis } from '@upstash/redis';
import { rateLimiter } from '@ambergristle/hono-rate-limiter';
import { FixedWindowCounter } from '@ambergristle/hono-rate-limiter/algorithms';

const globalGetLimiter = rateLimiter({
  client: Redis.fromEnv(), // don't forget to set env variables!
  algorithm: FixedWindowCounter.init(100, 60),
  cost: (c) => c.req.method === 'GET' ? 1 : 3,
  generateKey: (c) => getConnInfo(c).remote.address,
});

const app = new Hono()
  .use('*', globalGetLimiter)
  .get('/', (c) => c.text('Rate limited!'));

export default app;

Generate identifiers from Context variables

type AuthEnv = {
  Variables: {
    user: { userId: string };
  }
}

const userLimiter = rateLimiter<AuthEnv>({
  // ...
  generateKey: (c) => c.var.user.userId,
});

Initialize limiter from Context

type RedisEnv = {
  Variables: {
    client: Redis;
  }
}

rateLimiter<RedisEnv>({
  client: (c) => c.var.client,
  // ...
});

Make sure cache is persisted beyond requests

const blockedCache = new Map<string, number>();

rateLimiter<RedisEnv>({
  // ...
  blockedCache,
});

Configure response Headers and error Response

rateLimiter<RedisEnv>({
  // ...
  headerSpec: 'draft-7', // 'draft-6' | 'draft-7' | 'draft-8'
  refundFailedRequests: true,
  errorResponse: (c) => {
    return c.text('Slow down!', 420);
  }
});

Customize key prefixing

{keyPrefix?}:{policyName}:{key}

rateLimiter<RedisEnv>({
  // ...
  keyPrefix: 'limit',
  policyName: 'global',
});

Roadmap

improve tests, benchmarks

  • optimize test design to provide better coverage, use fewer resrouces
  • add coverage for algorithm-specific behaviors
  • add coverage/benchmarks for limiter performance

reset all

  • should be able to clear redis policy-prefixed records
  • should this exist at the RateLimiter level?

improve errors

  • distinguish between different error types?

open questions

  • how should request cost be communicated to clients
    • is it factored into

Future

deny list

  • allow identifiers? to be blacklisted
  • use a reference list as a starting point?

safe load scripts

  • look into prehashing scripts, should be ez
  • add preloading for less-likely scripts, try to load if not found

dynamic cost window log

  • doesn't obviously make sense, poses some serious technical challenges

dynamic refunds

  • only required if limiter is untethered from middleware

tests

[x] headers

[x] cache

middleware [] configuration (validate required arguments and outputs) [] headers [] error response

algorithms [x] configuration (validate required arguments and outputs) [x] basic [x] returns rate limit info and limiter result' [x] blocks after limit exceeded [x] allows additional requests after reset [x] works consistently (this is a weak test) [x] rejects all requests if max=0 [] specific [] fixed doubled at boundaries [] sliding smooth at boundaries [] log exact [] bucket? burst and rate [x] check: check method returns current rate limit info [x] refund: refund method restores quota units [x] reset: reset method deletes identifier bucket

performance [] response time [] resource use

  • concurrent requests
  • multiple users (limits isolated per id)
  • configurable windows