DurableObject rate-limiter


Sometimes you need to rate limit requests to your server. This DurableObject is a simple example of how to rate limit requests based on an arbitrary key. One durable-object instance is created per key allowing for distributed rate limiters. An example of how to use this durable-object can be found at /llm.

Show Code
import { zValidator } from "@hono/zod-validator";
import { Hono } from "hono";
import { z } from "zod";

const app = new Hono<{
  Bindings: { state: DurableObjectState };
}>().get(
  "/next",
  zValidator(
    "query",
    z.object({
      msPerRequest: z.coerce.number().min(1),
      msForGracePeriod: z.coerce.number().min(0),
    }),
  ),
  async (c) => {
    const { msForGracePeriod, msPerRequest } = c.req.valid("query");

    const value = await c.env.state.blockConcurrencyWhile(async () => {
      const now = Date.now();
      let nextAllowedTime =
        (await c.env.state.storage.get<number>("nextAllowedTime")) ?? now;

      nextAllowedTime = Math.max(now, nextAllowedTime);
      nextAllowedTime += msPerRequest;
      await c.env.state.storage.put("nextAllowedTime", nextAllowedTime);
      return Math.max(0, nextAllowedTime - now - msForGracePeriod);
    });

    return c.json(value);
  },
);

export type RateLimiterAPI = typeof app;

export class RateLimiter implements DurableObject {
  constructor(private state: DurableObjectState) {}

  async fetch(request: Request): Promise<Response> {
    return app.fetch(request, {
      state: this.state,
    });
  }
}