next.jscachingredisserver-action

How to Ratelimit (using IP Address) with Upstash using Nextjs 14 server actions


Question is really simple, everything is literally explained in the title of the post.

How to implement rate limiting using users IP Address with Server Actions?

I've written this code on purpose so it will have errors, so I can demonstrate.

"use server"

// NEXTJS IMPORTS
import { auth } from "@/auth";

// CONFIG
import { API_ERROR_MESSAGES } from "@/app/config";

// ACTIONS
import { getUserIdByUsername } from "@/app/actions/add_post/getUserIdByUsername";

// REDIS
import { getCache, setCache } from "@/app/actions/redis/redisFunctions";
import { Redis } from '@upstash/redis';
import { Ratelimit } from "@upstash/ratelimit";

// DATABASE
import { database } from "@/app/lib/database";

const ratelimit = new Ratelimit({
    redis: Redis.fromEnv(),
    limiter: Ratelimit.slidingWindow(1, "10 s")
})

export async function createMentions(description: string, postId: number, commentId?: number) {
    const ip = request.headers.get("x-forwarded-for") ?? ""; // HERE LIES THE ISSUE, WE CANNOT SET HEADERS LIKE THIS SINCE IT IS SERVER ACTION, AND WE DONT HAVE ACCESS TO REQUEST (As of my knowledge)
    const { success, reset } = await ratelimit.limit(ip);

    if(!success) {
        const now = Date.now();
        const retryAfter = Math.floor((reset - now) / 1000);

        /*

        THIS IS HOW IT WOULD LOOK LIKE IF WE WERE USING API ENDPOINT WITH NextResponse

        return new NextResponse("Too many requests", {
            status: 429,
            headers: {
                ["retry-after"]: `${retryAfter}`,
            },
        }); 

        */

        return { success: false, error: { code: "TOO_MANY_REQUESTS" }} // HOW I WANT IT TO BE
    }

return { success: true, error: { code: "SOME_SUCCESS_MESSAGE" }}

I tried researching this, but didn't find any solutions (PS: I actually did but it's not good). Since server actions are "new", there are not many topics about this.


Solution

  • You can use the headers() function from next/headers.

    https://github.com/vercel/next.js/issues/47793

    "use server";
    
    import { headers } from "next/headers";
    
    export async function getIp() {
      const ip = headers().get("x-forwarded-for");
    
      return ip;
    }