Setup
Hono middleware, route wrappers, and per-user rate limiting.
Typing Bindings and Variables
Pass your app's Hono Env to createRateLimit so Context in middleware and handler callbacks includes typed Bindings and Variables:
export interface Bindings {
TOKEN: string;
}
export interface Variables {
user: User;
}
export type AppEnv = { Bindings: Bindings; Variables: Variables };import { createRateLimit } from "@g14o/ratelimit-hono";
import { env } from "./env";
import type { AppEnv } from "../types";
export const {
middleware,
withRateLimit,
withUserRateLimit,
userMiddleware,
checkRateLimit,
} = createRateLimit<AppEnv>({
redis: {
url: env.UPSTASH_REDIS_REST_URL,
token: env.UPSTASH_REDIS_REST_TOKEN,
},
});Bind AppEnv once at the factory. All exported helpers (withRateLimit, middleware, identifierFn callbacks, etc.) use Context<AppEnv>.
Store configuration (recommended)
import { createRateLimit } from "@g14o/ratelimit-hono";
import { upstashStore } from "@g14o/ratelimit-hono/upstash";
export const { middleware, withRateLimit } = createRateLimit<AppEnv>({
store: upstashStore({
url: env.UPSTASH_REDIS_REST_URL,
token: env.UPSTASH_REDIS_REST_TOKEN,
}),
});Store helpers (memoryStore, upstashStore, createStore, defineStore) are exported from @g14o/ratelimit-hono and its /memory and /upstash subpaths. store and redis are mutually exclusive.
Create a client
Same as above — export the helpers you need from a single lib/ratelimit.ts module.
Route middleware (idiomatic Hono)
Built-in tier limits are listed in @g14o/ratelimit setup — default tiers.
import { Hono } from "hono";
import { middleware } from "./lib/ratelimit";
import type { AppEnv } from "./types";
const app = new Hono<AppEnv>();
app.get(
"/api/status",
middleware({ tier: "lenient" }),
(c) => c.json({ ok: true })
);Wrapped route handler
withRateLimit returns a MiddlewareHandler assignable to typed app.* routes. Handler callbacks receive your app's Context:
import { withRateLimit } from "./lib/ratelimit";
app.post(
"/api/chat",
withRateLimit(
(c) => {
const user = c.get("user");
const token = c.env.TOKEN;
return c.json({ ok: true, user, token });
},
{ tier: "moderate", prefix: "@ratelimit:chat" }
)
);Works the same in route modules — import withRateLimit from lib/ratelimit.ts.
Per-user limits
Use a verified identity from upstream auth middleware (session, JWT, etc.) — set on Variables.user, not client-controlled headers.
import { Hono } from "hono";
import { userMiddleware } from "../lib/ratelimit";
import type { AppEnv } from "../types";
const app = new Hono<AppEnv>();
app.post(
"/api/user-action",
userMiddleware(async (c) => c.get("user")?.id ?? null, {
tier: "auth",
}),
(c) => c.json({ ok: true })
);Manual check
Use checkRateLimit when you need full control over the response:
import { checkRateLimit } from "../lib/ratelimit";
import type { Context } from "../types";
export async function customHandler(c: Context) {
const result = await checkRateLimit(c, { tier: "strict" });
if (!result.ok) {
return c.json({ error: "Too many requests" }, 429);
}
return c.json({ ok: true });
}