TheSnowmann
TheSnowmann

Reputation: 41

NEXTJS - POST net::ERR_ABORTED 500 (Internal Server Error)

I am getting POST http://localhost:3000/api/user-profile net::ERR_ABORTED 500 (Internal Server Error) error message in developer console when trying to submit data to my Supabase Database. The terminal says TypeError: res.status is not a function at POST. This is my first time attempting to make an API route in NextJS so go easy.

Folder Structure:

project-root/
│
├── app/                                   
│   ├── api/                              
│   │   └── user-profile/                 
│   │       └── route.ts               
│   │
│   ├── user-profile/                     
│   │   └── page.tsx   
│   ├── types/     
│   │   └── userProfile.ts 
│   ├── utils/                                
│   │   └── client.ts                                 
│   │
│   ├── globals.css                          
│   └── page.tsx                          
│
├── public/                               
│
├── package.json                          
├── tsconfig.json                         

/app/api/user-profile/route.ts

import type { NextApiRequest, NextApiResponse } from 'next';
import { supabase } from '@/app/utils/client';
import { UserProfileUpdateData } from '@/app/types/userProfile';

// Named export function specifically for handling POST requests
export async function POST(req: NextApiRequest, res: NextApiResponse) {
    const {
        authUserId,
        name,
        email,
        age,
        gender,
        occupation,
        hourly_rate,
    }: UserProfileUpdateData = req.body;

    const { data: updateData, error: updateError } = await supabase
        .from('users')
        .update({ name, email, age, gender, occupation, hourly_rate })
        .eq('auth_user_id', authUserId);

    if (updateError) {
        return res.status(500).json({ error: updateError.message });
    }

    if (!updateData || (updateData as any[]).length === 0) {
        const { error: insertError } = await supabase.from('users').insert([
            {
                auth_user_id: authUserId,
                name,
                email,
                age,
                gender,
                occupation,
                hourly_rate,
                number_of_active_projects: 0,
                number_of_completed_projects: 0,
            },
        ]);

        if (insertError) {
            return res.status(500).json({ error: insertError.message });
        }
    }

    return res
        .status(200)
        .json({ message: 'User profile updated successfully' });
}

/app/(pages)/user-profile/page.tsx

'use client';

import React from 'react';
import { useForm, SubmitHandler } from 'react-hook-form';
import toast from 'react-hot-toast';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import DeveloperButtons from '@/app/components/DeveloperButtons';

const profileSchema = z.object({
    name: z.string().min(1, { message: 'Name is required' }),
    email: z.string().email({ message: 'Invalid email format' }),
    age: z.coerce
        .number()
        .int()
        .min(0, { message: 'Invalid age' })
        .max(100, { message: 'Invalid age' }),
    gender: z.string().min(1, { message: 'Gender is required' }),
    occupation: z.string(),
    hourly_rate: z.coerce
        .number()
        .int()
        .min(0, { message: 'Invalid hourly rate' }),
});

type ProfileFormData = z.infer<typeof profileSchema>;

const UserProfile = () => {
    const {
        register,
        handleSubmit,
        reset,
        formState: { errors },
    } = useForm<ProfileFormData>({
        resolver: zodResolver(profileSchema),
    });

    const onSubmit: SubmitHandler<ProfileFormData> = async (data) => {
        const response = await fetch('/api/user-profile/', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(data),
        });

        if (response.ok) {
            toast.success('Profile updated successfully!');
        } else {
            toast.error('An error occurred while updating the profile.');
        }
    };

    return (
        <div className='container mx-auto p-4'>
            <DeveloperButtons />
            <form
                onSubmit={handleSubmit(onSubmit)}
                className='form-control w-full max-w-xs pt-8'
            >
                <label className='label'>
                    <span className='label-text'>Name</span>
                </label>
                <input
                    type='text'
                    {...register('name')}
                    className='input input-bordered w-full max-w-xs'
                />
                {errors.name && (
                    <p className='text-red-500'>{errors.name.message}</p>
                )}

                <label className='label'>
                    <span className='label-text'>Email</span>
                </label>
                <input
                    type='email'
                    {...register('email')}
                    className='input input-bordered w-full max-w-xs'
                />
                {errors.email && (
                    <p className='text-red-500'>{errors.email.message}</p>
                )}

                <label className='label'>
                    <span className='label-text'>Age</span>
                </label>
                <input
                    type='number'
                    {...register('age')}
                    className='input input-bordered w-full max-w-xs'
                />
                {errors.age && (
                    <p className='text-red-500'>{errors.age.message}</p>
                )}

                <label className='label'>
                    <span className='label-text'>Gender</span>
                </label>
                <input
                    type='text'
                    {...register('gender')}
                    className='input input-bordered w-full max-w-xs'
                />
                {errors.gender && (
                    <p className='text-red-500'>{errors.gender.message}</p>
                )}

                <label className='label'>
                    <span className='label-text'>Occupation</span>
                </label>
                <input
                    type='text'
                    {...register('occupation')}
                    className='input input-bordered w-full max-w-xs'
                />
                {errors.occupation && (
                    <p className='text-red-500'>{errors.occupation?.message}</p>
                )}

                <label className='label'>
                    <span className='label-text'>Hourly Rate</span>
                </label>
                <input
                    type='number'
                    step='0.01'
                    {...register('hourly_rate')}
                    className='input input-bordered w-full max-w-xs'
                />
                {errors.hourly_rate && (
                    <p className='text-red-500'>{errors.hourly_rate.message}</p>
                )}

                <div className='flex space-x-2 justify-center my-4'>
                    <button
                        type='submit'
                        className='btn btn-primary'
                    >
                        Submit
                    </button>
                    <button
                        type='button'
                        onClick={() => reset()}
                        className='btn btn-secondary'
                    >
                        Reset
                    </button>
                </div>
            </form>
        </div>
    );
};

export default UserProfile;

My 'users' table on Supabase Schema:

user_id (primary key, non-nullable, int4), name (nullable varchar), email (non-nullable, unique, varchar), age (nullable, int4), gender (nullable varchar), occupation (nullable varchar), organization_id (nullable int4), hourly_rate (nullable int4), number_of_active_projects (nullable int4), number_of_completed_projects (nullable int4), auth_user_id (nullable uuid).

Got a little lazy on commas typing that but you get the point.

I am expecting the data to be inputted into the database but I am running into the 500 (internal server error) issue. I am suspecting that it has to deal with the body: JSON.stringify(data) aspect of the onSubmit but I am very new with apis so I could be totally wrong.

Update:

Ahmed suggested using Next/Response. Here is the updated route.ts: (I commented out the old returns)

import { NextRequest, NextResponse } from 'next/server';
import { supabase } from '@/app/utils/client';
import { UserProfileUpdateData } from '@/app/types/userProfile';

// Named export function specifically for handling POST requests
export async function POST(req: NextRequest /*, res: NextApiResponse */) {
    const {
        authUserId,
        name,
        email,
        age,
        gender,
        occupation,
        hourly_rate,
    }: UserProfileUpdateData = await req.json();

    const { data: updateData, error: updateError } = await supabase
        .from('users')
        .update({ name, email, age, gender, occupation, hourly_rate })
        .eq('auth_user_id', authUserId);

    if (updateError) {
        return NextResponse.json(
            { error: updateError.message },
            { status: 500 }
        );
        // return res.status(500).json({ error: updateError.message });
    }

    if (!updateData || (updateData as any[]).length === 0) {
        const { error: insertError } = await supabase.from('users').insert([
            {
                auth_user_id: authUserId,
                name,
                email,
                age,
                gender,
                occupation,
                hourly_rate,
                number_of_active_projects: 0,
                number_of_completed_projects: 0,
            },
        ]);

        if (insertError) {
            return NextResponse.json(
                { error: insertError.message },
                { status: 500 }
            );
            // return res.status(500).json({ error: insertError.message });
        }
    }

    return NextResponse.json({ message: 'success' }, { status: 201 });
    // return res
    //     .status(200)
    //     .json({ message: 'User profile updated successfully' });
}

In terminal it says it properly compiled /api/user-profile when I hit the submit button. In developer console it says POST http://localhost:3000/api/user-profile 500 (Internal Server Error) still.

Upvotes: 1

Views: 665

Answers (2)

TheSnowmann
TheSnowmann

Reputation: 41

My issue lied with all my attempts of getting the auth_user_id aka User UID from supabase authentication. I simplified my route.ts file to disregard that.

import { NextRequest, NextResponse } from 'next/server';
import { supabase } from '@/app/utils/client';

export async function POST(req: NextRequest) {
    let reqBody;
    try {
        reqBody = await req.json();
    } catch (error) {
        console.error('Error parsing JSON:', error);
        return NextResponse.json({ error: 'Bad Request' }, { status: 400 });
    }

    const { email, name, age, gender, occupation, hourly_rate } = reqBody;


    const { data: existingUserData, error: findUserError } = await supabase
        .from('users')
        .select()
        .eq('email', email)
        .single(); 

    if (findUserError) {
        console.error('Find user error:', findUserError);
        return NextResponse.json(
            { error: findUserError.message },
            { status: 500 }
        );
    }

    if (existingUserData) {
        const { error: updateError } = await supabase
            .from('users')
            .update({ name, age, gender, occupation, hourly_rate })
            .eq('email', email);

        if (updateError) {
            console.error('Update error:', updateError);
            return NextResponse.json(
                { error: updateError.message },
                { status: 500 }
            );
        }
    } else {
        const { error: insertError } = await supabase
            .from('users')
            .insert([{ email, name, age, gender, occupation, hourly_rate }]);

        if (insertError) {
            console.error('Insert error:', insertError);
            return NextResponse.json(
                { error: insertError.message },
                { status: 500 }
            );
        }
    }

    return NextResponse.json(
        { message: 'User profile processed successfully' },
        { status: 200 }
    );
}

Upvotes: 0

Ahmed Abdelbaset
Ahmed Abdelbaset

Reputation: 4926

Route handlers in the App Router has some updates:

  1. the type of the request is NextRequest not NextApiRequest.
import { NextRequest } from "next/server";

export async function POST(req: NextRequest) {}
  1. To get the body, use request.json() not request.body:
  const {
        authUserId,
        name,
        email,
        age,
        gender,
        occupation,
        hourly_rate,
    }: UserProfileUpdateData = await req.json();
  1. You have to always return a Response by:
  • return new Response("message", {status: 200})
  • return new NextResponse("message", {status: 200})
  • return Response.json({...}, {status: 200})
  • return NextResponse.json({...}, {status: 200})

This should work:

import { NextResponse, type NextRequest } from "next/server";

// Named export function specifically for handling POST requests
export async function POST(req: NextRequest) {
  const { authUserId, name, email, age, gender, occupation, hourly_rate } =
    await req.json();

  // ...;


  return NextResponse.json({ message: "success" }, { status: 201 });
}

Upvotes: 1

Related Questions