Skip to content

Commit 3f0b01d

Browse files
authored
chore: Add global rate limit (#582)
* chore: Add global rate limit * typo * merge conflict * fix build * expire key
1 parent 192194c commit 3f0b01d

File tree

3 files changed

+26
-0
lines changed

3 files changed

+26
-0
lines changed

src/server/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { withErrorHandler } from "./middleware/error";
1515
import { withExpress } from "./middleware/express";
1616
import { withRequestLogs } from "./middleware/logs";
1717
import { withOpenApi } from "./middleware/open-api";
18+
import { withRateLimit } from "./middleware/rateLimit";
1819
import { withWebSocket } from "./middleware/websocket";
1920
import { withRoutes } from "./routes";
2021
import { writeOpenApiToFile } from "./utils/openapi";
@@ -62,6 +63,7 @@ export const initServer = async () => {
6263
await withRequestLogs(server);
6364
await withErrorHandler(server);
6465
await withEnforceEngineMode(server);
66+
await withRateLimit(server);
6567
await withWebSocket(server);
6668
await withAuth(server);
6769
await withExpress(server);

src/server/middleware/rateLimit.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { FastifyInstance } from "fastify";
2+
import { StatusCodes } from "http-status-codes";
3+
import { env } from "../../utils/env";
4+
import { redis } from "../../utils/redis/redis";
5+
import { createCustomError } from "./error";
6+
7+
export const withRateLimit = async (server: FastifyInstance) => {
8+
server.addHook("onRequest", async (request, reply) => {
9+
const epochTimeInMinutes = Math.floor(new Date().getTime() / (1000 * 60));
10+
const key = `rate-limit:global:${epochTimeInMinutes}`;
11+
const count = await redis.incr(key);
12+
redis.expire(key, 2 * 60);
13+
14+
if (count > env.GLOBAL_RATE_LIMIT_PER_MIN) {
15+
throw createCustomError(
16+
`Too many requests. Please reduce your calls to ${env.GLOBAL_RATE_LIMIT_PER_MIN} requests/minute or update the "GLOBAL_RATE_LIMIT_PER_MIN" env var.`,
17+
StatusCodes.TOO_MANY_REQUESTS,
18+
"TOO_MANY_REQUESTS",
19+
);
20+
}
21+
});
22+
};

src/utils/env.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export const env = createEnv({
8282
ENGINE_MODE: z
8383
.enum(["default", "sandbox", "server_only", "worker_only"])
8484
.default("default"),
85+
GLOBAL_RATE_LIMIT_PER_MIN: z.coerce.number().default(400 * 60),
8586
},
8687
clientPrefix: "NEVER_USED",
8788
client: {},
@@ -108,6 +109,7 @@ export const env = createEnv({
108109
process.env.CONTRACT_SUBSCRIPTIONS_DELAY_SECONDS,
109110
REDIS_URL: process.env.REDIS_URL,
110111
ENGINE_MODE: process.env.ENGINE_MODE,
112+
GLOBAL_RATE_LIMIT_PER_MIN: process.env.GLOBAL_RATE_LIMIT_PER_MIN,
111113
},
112114
onValidationError: (error: ZodError) => {
113115
console.error(

0 commit comments

Comments
 (0)