Next.js Discord

Discord Forum

Security of /api/* code

Unanswered
Brown bear posted this in #help-forum
Open in Discord
Brown bearOP
Hello! I'm making a website that has a free tier for authenticated users which lets them call my api (/api/chartbot ) only once per user forever. My purpose is to publish this website so I am concerned about the security of this api route. Below is the code I use (for now) for this api:
export async function POST(req: NextRequest) {
    // Check if user is authenticated
    const supabase = createClient();
    const { data: { user }, error: userError } = await supabase.auth.getUser();

    if (userError || !user) {
        return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
    }

    // Check if user has exceeded API rate limit for free tier
    const { data, error: apiCallsError } = await supabase
        .from('users')
        .select('api_calls')
        .eq('id', user.id)
        .single();

    if (apiCallsError || !data) {
        return NextResponse.json({ error: "Failed to retrieve API calls" }, { status: 500 });
    }

    const { api_calls } = data;

    if (api_calls >= 1) {
        return NextResponse.json({ error: "API rate limit exceeded" }, { status: 429 });
    }

    const body = await req.json();
    const chart_response = await generateChart(body);

    // Update user's API calls
    const { error: updateError } = await supabase
        .from('users')
        .update({ api_calls: api_calls + 1 })
        .eq('id', user.id);

    if (updateError) {
        return NextResponse.json({ error: "Failed to update API calls" }, { status: 500 });
    }

    return NextResponse.json(chart_response);
}


I dont want it to be vulnerable to attacks. Some of my questions are: 1. From what I read, no one has access to the code in /api folder. Is this true? 2. What are some good practices for securing or limiting the number of requests of my api? 3. Considering this might be hosted on Vercel, should I add a limit for each IP so I dont get bots spamming my api or is this something vercel deals with already?

13 Replies

@Brown bear Hello! I'm making a website that has a free tier for authenticated users which lets them call my api (`/api/chartbot` ) only once per user forever. My purpose is to publish this website so I am concerned about the security of this api route. Below is the code I use (for now) for this api: export async function POST(req: NextRequest) { // Check if user is authenticated const supabase = createClient(); const { data: { user }, error: userError } = await supabase.auth.getUser(); if (userError || !user) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } // Check if user has exceeded API rate limit for free tier const { data, error: apiCallsError } = await supabase .from('users') .select('api_calls') .eq('id', user.id) .single(); if (apiCallsError || !data) { return NextResponse.json({ error: "Failed to retrieve API calls" }, { status: 500 }); } const { api_calls } = data; if (api_calls >= 1) { return NextResponse.json({ error: "API rate limit exceeded" }, { status: 429 }); } const body = await req.json(); const chart_response = await generateChart(body); // Update user's API calls const { error: updateError } = await supabase .from('users') .update({ api_calls: api_calls + 1 }) .eq('id', user.id); if (updateError) { return NextResponse.json({ error: "Failed to update API calls" }, { status: 500 }); } return NextResponse.json(chart_response); } I dont want it to be vulnerable to attacks. Some of my questions are: 1. From what I read, no one has access to the code in /api folder. Is this true? 2. What are some good practices for securing or limiting the number of requests of my api? 3. Considering this might be hosted on Vercel, should I add a limit for each IP so I dont get bots spamming my api or is this something vercel deals with already?
It's a bit misunderstood, the /api folder is just like any other route in your app. Specifically, Route handlers make the code server side only.
Limiting your endpoint just by getting the IP is a very very bad idea. To actually have rate-limiting, implement authentication and use API keys
And yes, Vercel deals with bots
I would suggest routing to your vercel with a custom domain and cloudflare
So u have even more control
Brown bearOP
So if you said that the code is server side only, doesnt this mean no one can modify the code? Thus, when someone calls the api (even from outside the app) they still have to go through the steps in my code (verifying if he is logged in and if he has less than 1 api_calls made so far) ?
the exported POST function in your .ts file is what makes it a route handler
if u define everything else in that file, it will be serverside aswell, since everything is on the server in nextjs
by default*
Brown bearOP
Agreed. So what issues would there be if i keep it like this?
Chub mackerel
your route handler looks secure, but you have some mistakes
you are update api_calls from db fields this will cause Race Condition which means user can send request with different device. for solve this problem use atomic_updatesin Supabase:
const { error: updateError } = await supabase
    .from('users')
    .update({ api_calls: supabase.raw('api_calls + 1') })
    .eq('id', user.id)
    .is('api_calls', api_calls);
and you should do validation on body before passing it to generateChart
const body = await req.json();
if (!body || !body.chartType || typeof body.data !== "object") {
    return NextResponse.json({ error: "Invalid input" }, { status: 400 });
}
Brown bearOP
Really good points, thank you!