Extras
Rate Limits (API Calls)
For plans that introduce a rate limit (e.g. allowing a user to upload 50 files per month), it’s recommended to limit the number of API calls a user can perform in a given time frame based on their IP address. In this example, we’ll be limiting it to 5 API calls per minute using Upstash.
You can find an example of how the middleware.ts
file should look like at the end of this
page.
-
You can create a new Upstash account [here]
-
Create a Redis database.
-
In the details section of the dashboard, get the
UPSTASH_REDIS_REST_URL
andUPSTASH_REDIS_REST_TOKEN
-
Let’s now update the following variables inside the
.env.local
in the codebase:
UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN=
- In the codebase, run the following command to install the
@upstash/ratelimit
package:
npm install @upstash/redis @upstash/ratelimit
- In the
middleware.ts
file, add anRATE_LIMITED_URLS
arr outside of the middleware function. This arr contains the URLs that will be rate limited.
const RATE_LIMITED_URLS = ["/api/example-call"];
- Then, initialise the redis client with the
.env
variables we just copied:
const redis = new Redis({
url: process.env.NEXT_PUBLIC_UPSTASH_REDIS_REST_URL,
token: process.env.NEXT_PUBLIC_UPSTASH_REDIS_REST_TOKEN,
});
- You can now add the
rateLimit
outside of the middleware function. In our case, we’ll be limiting it to 5 API calls per minute (60 seconds). If a user makes more than 5 API calls in a minute, a429
response will be returned.
const ratelimit = new Ratelimit({
redis: redis,
limiter: Ratelimit.slidingWindow(5, "60 s"),
});
- You’ll just have to add this if statement to check if the URL is rate limited and return the appropriate response and then we’re done.
if (RATE_LIMITED_URLS.some((url) => request.nextUrl.pathname.startsWith(url))) {
const ip = request.ip ?? "127.0.0.1";
const { success } = await ratelimit.limit(ip);
if (!success) {
return new NextResponse("Too Many Requests", { status: 429 });
}
}
Please add this if statement inside the middleware function BUT before the
supabaseClient.auth.getUser()
function. Otherwise, the rate limit will not work as it will be
executed after all the API calls have been made.
Here’s an example of how the middleware.ts
file should look like:
import { Redis } from "@upstash/redis";
import { Ratelimit } from "@upstash/ratelimit";
const RATE_LIMITED_URLS = ["/api/example-call"];
...
const redis = new Redis({
url: process.env.NEXT_PUBLIC_UPSTASH_REDIS_REST_URL,
token: process.env.NEXT_PUBLIC_UPSTASH_REDIS_REST_TOKEN,
});
const ratelimit = new Ratelimit({
redis: redis,
limiter: Ratelimit.slidingWindow(5, "60 s"),
});
export const middleware = async (request: nextRequest) => {
...
if (RATE_LIMITED_URLS.some((url) => request.nextUrl.pathname.startsWith(url))) {
const ip = request.ip ?? "127.0.0.1";
const { success } = await ratelimit.limit(ip);
if (!success) {
return new NextResponse("Too Many Requests", { status: 429 });
}
}
const {
data: { user },
} = await supabaseClient.auth.getUser();
if (_isPathExcludedFromRouting(request.nextUrl.pathname)) {
return NextResponse.next({ request }); // exclude api routes from routing
}
if (isPublicRoute(request.nextUrl.pathname)) {
return NextResponse.next({ request }); // exclude public routes from routing
}
const response = await handleRouting(request, user as SupabaseUser);
if (response) return response;
...
};