-
-
Save pbteja1998/8a37d39a73013877decfab04bda32cf7 to your computer and use it in GitHub Desktop.
Using Clerk with Upstash for Middleware rate limiting and API Protection
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { getAuth, withClerkMiddleware } from "@clerk/nextjs/server"; | |
import { NextResponse, NextFetchEvent } from "next/server"; | |
import type { NextRequest } from "next/server"; | |
import { Ratelimit } from "@upstash/ratelimit"; | |
import { Redis } from "@upstash/redis"; | |
const publicPaths = ["/", "/sign-in*", "/sign-up*", "/api/blocked"]; | |
const ratelimit = new Ratelimit({ | |
redis: Redis.fromEnv(), | |
limiter: Ratelimit.cachedFixedWindow(1, "10s"), | |
ephemeralCache: new Map(), | |
analytics: true, | |
}); | |
const isPublic = (path: string) => { | |
return publicPaths.find((x) => | |
path.match(new RegExp(`^${x}$`.replace("*$", "($|/|\\.)"))) | |
); | |
}; | |
const isAPI = (path: string) => { | |
return path.match(new RegExp(`^\/api\/`)) | |
} | |
export default withClerkMiddleware(async (request: NextRequest, event: NextFetchEvent) => { | |
if (isAPI(request.nextUrl.pathname) && request.nextUrl.pathname !== '/api/blocked') { | |
const ip = request.ip; | |
const { success, pending, limit, reset, remaining } = await ratelimit.limit(`ratelimit_middleware_${ip}`); | |
event.waitUntil(pending); | |
const res = success ? NextResponse.next() : NextResponse.redirect(new URL("/api/blocked", request.url)); | |
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; | |
} | |
if (isPublic(request.nextUrl.pathname)) { | |
return NextResponse.next(); | |
} | |
// if the user is not signed in redirect them to the sign in page. | |
const { userId } = getAuth(request); | |
if (!userId) { | |
// redirect the users to /pages/sign-in/[[...index]].ts | |
const signInUrl = new URL("/sign-in", request.url); | |
signInUrl.searchParams.set("redirect_url", request.url); | |
return NextResponse.redirect(signInUrl); | |
} | |
return NextResponse.next(); | |
}); | |
// Stop Middleware running on static files | |
export const config = { | |
matcher: [ | |
/* | |
* Match all request paths except for the ones starting with: | |
* - _next | |
* - static (static files) | |
* - favicon.ico (favicon file) | |
*/ | |
"/(.*?trpc.*?|(?!static|.*\\..*|_next|favicon.ico).*)", | |
"/" | |
], | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment