Skip to content

Commit 134a1e1

Browse files
committed
feat: add rate limiting functionality to waitlist router
1 parent dfa7fbf commit 134a1e1

6 files changed

Lines changed: 33 additions & 0 deletions

File tree

apps/api/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@
2222
},
2323
"dependencies": {
2424
"@asyncstatus/email": "workspace:*",
25+
"@hono-rate-limiter/cloudflare": "0.2.2",
2526
"@hono/zod-validator": "0.4.3",
2627
"@libsql/client": "0.15.2",
2728
"better-auth": "1.2.5",
2829
"dayjs": "1.11.13",
2930
"drizzle-orm": "0.41.0",
3031
"hono": "4.7.6",
32+
"hono-rate-limiter": "0.4.2",
3133
"resend": "4.2.0"
3234
},
3335
"devDependencies": {

apps/api/src/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { invitationRouter } from "./routers/invitation";
1414
import { memberRouter } from "./routers/organization/member";
1515
import { organizationRouter } from "./routers/organization/organization";
1616
import { waitlistRouter } from "./routers/waitlist";
17+
import { createRateLimiter } from "./lib/rate-limiter";
1718

1819
const app = new Hono<HonoEnv>()
1920
.use(
@@ -36,6 +37,11 @@ const app = new Hono<HonoEnv>()
3637
c.set("resend", resend);
3738
const auth = createAuth(c.env, db, resend);
3839
c.set("auth", auth);
40+
const waitlistRateLimiter = createRateLimiter(c.env, {
41+
windowMs: 60 * 60 * 1000, // 60 minutes
42+
limit: 10, // Limit each IP to 100 requests per `window` (here, per 15 minutes).
43+
});
44+
c.set("auth", auth);
3945
const session = await auth.api.getSession({ headers: c.req.raw.headers });
4046
if (!session) {
4147
c.set("session", null);

apps/api/src/lib/env.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { Resend } from "resend";
44
import type { Db } from "../db";
55
import * as schema from "../db/schema";
66
import type { Auth } from "./auth";
7+
import type { RateLimiter } from "./rate-limiter";
78

89
export type Bindings = {
910
TURSO_URL: string;
@@ -18,13 +19,15 @@ export type Bindings = {
1819
RESEND_API_KEY: string;
1920
WEB_APP_URL: string;
2021
PRIVATE_BUCKET: R2Bucket;
22+
RATE_LIMITER: KVNamespace;
2123
};
2224

2325
export type Variables = {
2426
auth: Auth;
2527
db: Db;
2628
resend: Resend;
2729
session: Auth["$Infer"]["Session"] | null;
30+
waitlistRateLimiter: RateLimiter;
2831
};
2932

3033
export type HonoEnv = {

apps/api/src/lib/rate-limiter.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import type { HonoEnv } from "./env";
2+
import { WorkersKVStore } from "@hono-rate-limiter/cloudflare";
3+
import { rateLimiter } from "hono-rate-limiter";
4+
5+
export function createRateLimiter(env: HonoEnv["Bindings"], options: {windowMs: number; limit: number}) {
6+
return rateLimiter<HonoEnv>({
7+
windowMs: options.windowMs, // 15 minutes
8+
limit: options.limit, // Limit each IP to 100 requests per `window` (here, per 15 minutes).
9+
standardHeaders: "draft-6", // draft-6: `RateLimit-*` headers; draft-7: combined `RateLimit` header
10+
keyGenerator: (c) => c.req.header("cf-connecting-ip") ?? "", // Method to generate custom identifiers for clients.
11+
store: new WorkersKVStore({ namespace: env.RATE_LIMITER }), // Here CACHE is your WorkersKV Binding.
12+
})
13+
};
14+
15+
export type RateLimiter = ReturnType<typeof createRateLimiter>;

apps/api/src/routers/waitlist.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { zCreateWaitlistUser } from "../schema/waitlist";
1111
export const waitlistRouter = new Hono<HonoEnv>().post(
1212
"/",
1313
zValidator("json", zCreateWaitlistUser),
14+
async (c, next) => {c.var.waitlistRateLimiter(c, next)},
1415
async (c) => {
1516
const { firstName, lastName, email } = c.req.valid("json");
1617
const existingUser = await c.var.db.query.user.findFirst({

bun.lock

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)