diff --git a/.gitignore b/.gitignore index b0f41ec..3e36f85 100644 --- a/.gitignore +++ b/.gitignore @@ -3,10 +3,11 @@ dist .env redis-server -./**/yarn.lock -./**/package-lock.json -./**/pnpm-lock.yaml +**/yarn.lock +**/package-lock.json +**/pnpm-lock.yaml *.log .vercel -.next \ No newline at end of file +.next + diff --git a/examples/nextjs/middleware.ts b/examples/nextjs/middleware.ts new file mode 100644 index 0000000..ac62ce1 --- /dev/null +++ b/examples/nextjs/middleware.ts @@ -0,0 +1,33 @@ +import { NextFetchEvent, NextRequest, NextResponse } from "next/server"; +import { Ratelimit } from "@upstash/ratelimit"; +import { Redis } from "@upstash/redis"; + +const ratelimit = new Ratelimit({ + redis: Redis.fromEnv(), + limiter: Ratelimit.fixedWindow(10, "10 s"), +}); + +export default async function middleware( + request: NextRequest, + event: NextFetchEvent, +): Promise { + const ip = request.ip ?? "127.0.0.1"; + + const { success, pending, limit, reset, remaining } = await ratelimit.limit( + `mw_${ip}`, + ); + event.waitUntil(pending); + + const res = success + ? NextResponse.next(request) + : NextResponse.rewrite(new URL("/api/blocked", request.url), request); + + res.headers.set("X-RateLimit-Limit", limit.toString()); + res.headers.set("X-RateLimit-Remaining", remaining.toString()); + res.headers.set("X-RateLimit-Reset", reset.toString()); + return res; +} + +export const config = { + matcher: "/api/hello", +}; diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json index b24adc2..a154b17 100644 --- a/examples/nextjs/package.json +++ b/examples/nextjs/package.json @@ -8,8 +8,8 @@ }, "dependencies": { "@upstash/ratelimit": "../../dist", - "@upstash/redis": "^1.4.0", - "next": "^12.1.6", + "@upstash/redis": "latest", + "next": "^12.2.5", "react": "^18.1.0", "react-dom": "^18.1.0" }, diff --git a/examples/nextjs/pages/api/_middleware.ts b/examples/nextjs/pages/api/_middleware.ts deleted file mode 100644 index 77cace3..0000000 --- a/examples/nextjs/pages/api/_middleware.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { NextFetchEvent, NextRequest } from "next/server"; -import { MultiRegionRatelimit } from "@upstash/ratelimit"; -import { Redis } from "@upstash/redis"; - -const ratelimit = new MultiRegionRatelimit({ - redis: [ - new Redis({ - url: process.env.UPSTASH_REDIS_REST_URL_FRA!, - token: process.env.UPSTASH_REDIS_REST_TOKEN_FRA!, - }), - new Redis({ - url: process.env.UPSTASH_REDIS_REST_URL_IOWA!, - token: process.env.UPSTASH_REDIS_REST_TOKEN_IOWA!, - }), - ], - limiter: MultiRegionRatelimit.fixedWindow(10, "10 s"), -}); - -export default async function middleware( - request: NextRequest, - event: NextFetchEvent, -): Promise { - const start = Date.now(); - - const ip = request.ip ?? "127.0.0.1"; - - const { success, pending, limit, reset, remaining } = await ratelimit.limit( - `mw_${ip}`, - ); - event.waitUntil(pending); - console.log("Middleware", success); - return new Response( - JSON.stringify({ success, latency: Date.now() - start, ip }), - { - status: success ? 200 : 429, - headers: { - "Content-Type": "application/json", - "X-RateLimit-Limit": limit.toString(), - "X-RateLimit-Remaining": remaining.toString(), - "X-RateLimit-Reset": reset.toString(), - }, - }, - ); -} diff --git a/examples/nextjs/pages/api/blocked.ts b/examples/nextjs/pages/api/blocked.ts new file mode 100644 index 0000000..80954d6 --- /dev/null +++ b/examples/nextjs/pages/api/blocked.ts @@ -0,0 +1,9 @@ +import type { NextApiRequest, NextApiResponse } from "next"; + +export default function handler( + _req: NextApiRequest, + res: NextApiResponse, +) { + res.status(429); + return res.end(); +} diff --git a/examples/nextjs/pages/api/hello.ts b/examples/nextjs/pages/api/hello.ts new file mode 100644 index 0000000..665da29 --- /dev/null +++ b/examples/nextjs/pages/api/hello.ts @@ -0,0 +1,8 @@ +import type { NextApiRequest, NextApiResponse } from "next"; + +export default function handler( + _req: NextApiRequest, + res: NextApiResponse, +) { + return res.json({ hello: "world" }); +} diff --git a/examples/nextjs/pages/index.tsx b/examples/nextjs/pages/index.tsx index 271ff4f..0b39289 100644 --- a/examples/nextjs/pages/index.tsx +++ b/examples/nextjs/pages/index.tsx @@ -1,4 +1,5 @@ import type { NextPage } from "next"; +import { useRouter } from "next/router"; import { useEffect, useState } from "react"; const Home: NextPage = () => { @@ -6,10 +7,11 @@ const Home: NextPage = () => { null, ); + const router = useRouter(); useEffect(() => {}, []); const generate = async () => { - const res = await fetch("/api"); + const res = await fetch("/api/hello"); if (res.ok) { setResponse({ @@ -24,6 +26,7 @@ const Home: NextPage = () => { } else { console.log(JSON.stringify(res.headers, null, 2)); setResponse(null); + alert( `Ratelimit reached, try again after ${ new Date(