Reputation: 41
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
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
Reputation: 4926
Route handlers in the App Router has some updates:
NextRequest
not NextApiRequest
.import { NextRequest } from "next/server";
export async function POST(req: NextRequest) {}
request.json()
not request.body
: const {
authUserId,
name,
email,
age,
gender,
occupation,
hourly_rate,
}: UserProfileUpdateData = await req.json();
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