hono-rate-limiter

repository·main·Indexed 20 days ago

https://github.com/rhinobase/hono-rate-limiter

A lightweight rate limiting middleware for the Hono web framework designed to protect APIs from abuse. It supports multiple storage backends including MemoryStore, RedisStore, and UnstorageStore, and provides native integration for Cloudflare rate-limiting bindings. The library includes utilities for setting rate limit headers following IETF Draft 6 and Draft 7 specifications, as well as a specialized webSocketLimiter for WebSocket connections.

Tokens
5.3K
Snippets
17
Records
26
Agent score
71%

What's inside hono-rate-limiter

  1. Install and use Hono Rate Limiter

    main
    Hono Rate Limiter is a simple rate limiting middleware designed for the Hono web framework. It allows you to restrict the number of requests a user or IP can make to your API within a specific timeframe to prevent abuse and ensure service stability.
  2. Configure webSocketLimiter options

    main

    The webSocketLimiter accepts a WSConfigProps object to customize rate-limiting behavior.

    OptionTypeDefaultDescription
    limitnumber | ((c: Context) => Promise<number>)5The maximum number of allowed hits. Can be a static number or a function returning a limit based on context.
    windowMsnumber60_000The time window in milliseconds for the rate limit.
    messagestring'Too many requests...'The message sent when the limit is exceeded.
    statusCodenumber1008The WebSocket close code used when the limit is exceeded.
    keyGenerator(c: Context) => Promise<string>RequiredA function to generate a unique identifier (key) for the client (e.g., from IP or headers).
    skip(event: any, ws: any) => Promise<boolean>() => falseA function to determine if a specific message should be ignored by the rate limiter.
    handler(event: any, ws: any, options: any) => Promise<void>async (_, ws, opts) => ws.close(opts.statusCode, opts.message)The function called when the limit is exceeded.
    storeStorenew MemoryStore()The storage engine used to track hits (e.g., MemoryStore).
    requestPropertyNamestring'rateLimit'The key used to store RateLimitInfo in the Hono context.
    requestStorePropertyNamestring'rateLimitStore'The key used to store the store's interface (getKey, resetKey) in the Hono context.
    skipFailedRequestsbooleanfalseIf true, failed message processing will decrement the hit counter.
    skipSuccessfulRequestsbooleanfalseIf true, successful message processing will decrement the hit counter.
  3. Configure the Redis store options

    main

    When using the Redis store, you must provide an Options object. The configuration includes the Redis client, an optional key prefix, and an expiry reset setting.

    • client: An object implementing the RedisClient interface (required).
    • prefix: A string prepended to all keys stored in Redis (optional).
    • resetExpiryOnChange: A boolean that determines whether the key's expiry is reset every time the hit count changes (optional).
    const options: Options = {
      client: myRedisClient,
      prefix: 'rate-limit:',
      resetExpiryOnChange: true
    };
  4. Configure Cloudflare rateLimiter via binding

    main

    If you are deploying to Cloudflare and want to use Cloudflare's native rate-limiting capabilities, provide a binding in the configuration. The rateLimiter function will detect this and switch to cloudflareRateLimiter mode.

    Cloudflare Configuration Options:

    • binding: The Cloudflare rate-limiting binding. This can be the binding object itself or a function (c) => binding that retrieves it from the context.
    • keyGenerator: A function to generate the key used by Cloudflare's engine.
    • skip: A function to skip rate limiting for certain requests.
    • handler: Custom logic to execute when the limit is exceeded.
    • message: The response message/object when limited.
    • statusCode: The HTTP status code returned when limited.
    // Example using a Cloudflare binding
    app.use(
      '/',
      rateLimiter({
        binding: 'MY_RATE_LIMIT_BINDING', // or a function to get it
        keyGenerator: (c) => c.req.header('x-real-ip'),
      })
    );
  5. Configure UnstorageStore options

    main

    When instantiating UnstorageStore, you can provide the following configuration options in the constructor:

    OptionTypeDescription
    storageUnstorageInstanceRequired. An object containing get, set, and remove methods.
    prefixstringOptional. A string prepended to all keys in the storage. Defaults to hrl:
    const store = new UnstorageStore({
      storage: myStorage,
      prefix: 'custom-prefix:'
    });
  6. Configure Hono rateLimiter options

    main

    When using the standard Hono implementation, you can pass a ConfigProps object to rateLimiter to customize behavior:

    OptionTypeDefaultDescription
    windowMsnumber60_000The duration of the rate-limiting window in milliseconds.
    limitnumber or (c: Context) => number5The maximum number of hits allowed within the window. Can be a function for dynamic limits.
    messagestring or (c: Context) => string | object'Too many requests...'The response message or JSON object when the limit is exceeded.
    statusCodenumber429The HTTP status code returned when rate-limited.
    standardHeaders'draft-6' | 'draft-7' | booleanWhether to include RateLimit-* headers. Defaults to 'draft-6'.
    requestPropertyNamestring'rateLimit'The key used to store RateLimitInfo in the Hono context via c.get().
    requestStorePropertyNamestring'rateLimitStore'The key used to store the store's interface in the Hono context via c.get().
    skip(c: Context) => boolean() => falseA function that returns true to skip rate limiting for a specific request.
    keyGenerator(c: Context) => stringRequiredA function that generates a unique identifier (key) for the client (e.g., IP address).
    skipFailedRequestsbooleanfalseIf true, failed requests (based on requestWasSuccessful) will not count towards the limit.
    skipSuccessfulRequestsbooleanfalseIf true, successful requests will not count towards the limit.
    requestWasSuccessful(c: Context) => boolean(c) => c.res.status < 400Determines if a request is considered successful for the purpose of skipFailedRequests/skipSuccessfulRequests.
    handler(c, next, options) => Promise<Response>Default handlerCustom logic to execute when the rate limit is exceeded.
    storeStorenew MemoryStore()The storage engine used to track hits.
  7. Configure RedisStore options

    main

    When instantiating RedisStore, you can provide the following options:

    OptionTypeDefaultDescription
    clientRedisClientRequiredAn instance of a Redis client.
    prefixstring'hrl:'A string prepended to all keys stored in Redis to avoid collisions.
    resetExpiryOnChangebooleanfalseIf true, the key's expiry is reset every time the hit count changes (sliding window behavior). If false, the expiry is set once when the key is first created (fixed window behavior).
    const store = new RedisStore({
      client: redisClient,
      prefix: 'custom-prefix:',
      resetExpiryOnChange: true
    });
  8. Configure Hono Rate Limiter middleware

    main

    When using the standard Hono rate limiter, you can provide a HonoConfigType object. Key configuration options include:

    • windowMs: Duration (in ms) to track requests. Defaults to 60000 (1 minute).
    • limit: Maximum connections allowed in the window. Can be a number or a function (c) => Promisify<number>.
    • standardHeaders: Enables standardized rate limit headers. Supports true, false, `
  9. Use UnstorageStore for rate limiting with Unstorage

    main

    The UnstorageStore class allows you to use Unstorage as the backend for hono-rate-limiter. This is useful if you want to persist rate limit data across different storage engines (like Redis, Cloudflare KV, or memory) using the Unstorage API.

    To use it, you must provide an object that implements the UnstorageInstance interface, which requires get, set, and remove methods.

    import { UnstorageStore, type UnstorageInstance } from 'hono-rate-limiter';
    
    // 1. Define your Unstorage-compatible instance
    const myStorage: UnstorageInstance = {
      get: async (key) => { /* implementation */ },
      set: async (key, value) => { /* implementation */ },
      remove: async (key) => { /* implementation */ },
    };
    
    // 2. Initialize the store
    const store = new UnstorageStore({
      storage: myStorage,
      prefix: 'my-app-rl:', // Optional: defaults to 'hrl:'
    });
  10. RedisStore public API methods

    main

    The RedisStore class provides the following methods for managing rate limit state:

    • init(options: RateLimitConfiguration<E, P, I>): void: Initializes the store with the rate limiter's configuration (e.g., setting windowMs).
    • get(key: string): Promise<ClientRateLimitInfo | undefined>: Fetches the current hit count and resetTime for a specific key.
    • increment(key: string): Promise<ClientRateLimitInfo>: Atomically increments the hit count for a key and returns the updated ClientRateLimitInfo.
    • decrement(key: string): Promise<void>: Decrements the hit count for a key using the Redis DECR command.
    • resetKey(key: string): Promise<void>: Deletes the key from Redis, effectively resetting the rate limit for that identifier.
  11. Set the Retry-After header

    main

    Use setRetryAfterHeader to set the standard Retry-After header on a Hono response. This informs the client how many seconds to wait before retrying the request.

    The header value is calculated based on the resetTime provided in the RateLimitInfo object or the windowMs duration.

    import { setRetryAfterHeader } from "hono-rate-limiter";
    
    // Inside a Hono handler:
    setRetryAfterHeader(context, info, windowMs);