▼Environment (taken from packege.json)
"@prisma/client": "^4.11.0"
"@supabase/supabase-js": "^2.12.1"
"next":"^12.2.5",.
"next-auth": "^4.20.1",.
"react": "17.0.2", "next".
"typescript": "4.5.5"
The framework is nextjs
Database is supabase
Image storage is supabase storage
ORM is prisma
The form is react hook form
typescript is used.
I can register, update, and retrieve images without problems during local development with npm run dev.
If I run npm run build locally instead of vercel, I can build without any problem, npm run start works without any problem.
The column type of the image to be stored in the DB is string.
I think the error is because the type defined in next.js does not match the type when saving in prisma, but I am inexperienced and it is difficult to identify the cause and solve the problem.
Translated with www.DeepL.com/Translator (free version)
The following error message is output only when building with vercel, and I cannot build.
// Error output in vercel log
./pages/api/user/index.tsx:19:9
Type error: Type '{ name: any; email: any; password: any; image: any; }' is not assignable to type '(Without<UserCreateInput, UserUncheckedCreateInput> & UserUncheckedCreateInput) | (Without<...> & UserCreateInput)'.
Object literal may only specify known properties, and 'image' does not exist in type '(Without<UserCreateInput, UserUncheckedCreateInput> & UserUncheckedCreateInput) | (Without<...> & UserCreateInput)'.
Error api
import prisma from "../../../lib/prisma";
import type { NextApiRequest, NextApiResponse } from "next";
import { hash } from "bcrypt";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method === "POST") {
const { name, email, password, image } = req.body;
console.log(req.body);
const hash_password = await hash(password, 10);
const data = await prisma.user.create({
data: {
name: name,
email: email,
password: hash_password,
image: image,
},
});
return res.status(200).json(data);
} else if (req.method === "GET") {
const data = await prisma.user.findMany({});
return res.status(200).json(data);
}
}
component that calls the above API
// pages/user/index.tsx
import { useForm } from "react-hook-form";
import { useRouter } from "next/router";
import Link from "next/link";
import { signIn } from "next-auth/react";
import Image, { StaticImageData } from "next/image";
import { useState, useRef } from "react";
import { supabase } from "../../lib/supabase";
// ユーザのデフォルト画像
const defaultImg = process.env.NEXT_PUBLIC_DEFAULT_IMG;
interface UserInput {
name: string;
email: string;
password: string;
image?: string | StaticImageData;
}
type uploadImageUrl = string;
export default function UserForm() {
const [uploadImageUrl, setUploadImageUrl] = useState<uploadImageUrl>();
const [uploadImageFile, setUploadImageFile] = useState<File>();
const router = useRouter();
const {
register,
handleSubmit,
formState: { errors },
} = useForm<UserInput>({
defaultValues: {
name: "",
email: "",
password: "",
image: defaultImg,
},
});
// const inputRef = useRef<HTMLInputElement>();
// 画像が選択されたら、プレビュー
const onChangeFile = (e: React.ChangeEvent<HTMLInputElement>) => {
const { files } = e.target;
setUploadImageUrl(URL.createObjectURL(files[0]));
setUploadImageFile(files[0]);
};
const submitUserRegister = async (input: UserInput) => {
//supabaseに画像をアップロード
const { data, error } = await supabase.storage
.from("photos")
.upload("user/" + uploadImageFile.name, uploadImageFile);
//supabaseから画像のURLをDL
const url = await supabase.storage
.from("photos")
.getPublicUrl(JSON.stringify(data));
const { publicUrl } = url.data;
// DLしたURLをimageに格納
const formData = {
name: input.name,
email: input.email,
password: input.password,
image: publicUrl,
};
try {
const res = await fetch("/api/user", {
method: "POST",
body: JSON.stringify(formData),
headers: {
"Content-Type": "application/json",
},
});
// json形式で返す
const data = await res.json();
const email = data.email;
//登録済みのデータを使用するとhash化したpasswordを利用してしまうため、formに入力されたpasswordを使用
const password = formData.password;
//sign In()でそのままログイン
await signIn("credentials", {
email,
password,
callbackUrl: "/",
redirect: true,
});
} catch (error) {
console.error("Error registering user:", error);
}
};
return (
<div>
<h3>ユーザー登録</h3>
<form
onSubmit={handleSubmit(submitUserRegister)}
className="w-full max-w-sm"
>
---
不要部分の為省略
---
<Image
src={uploadImageUrl ? uploadImageUrl : defaultImg}
width={100}
height={100}
></Image>
<label className="block mb-2 text-sm font-medium text-gray-900 dark:text-white">
Upload file
</label>
<input
className="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg cursor-pointer bg-gray-50 dark:text-gray-400 focus:outline-none dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400"
id="file_input"
type="file"
onChange={onChangeFile}
/>
<div className="md:flex md:items-center">
<div className="md:w-2/3">
<button className="shadow bg-blue-500 hover:bg-blue-400 focus:shadow-outline focus:outline-none text-white font-bold py-2 px-4 rounded">
<Link href={`/`}>Home</Link>
</button>
</div>
<div className="md:w-2/3">
<button
className="shadow bg-blue-500 hover:bg-blue-400 focus:shadow-outline focus:outline-none text-white font-bold py-2 px-4 rounded"
type="submit"
>
Sign Up
</button>
</div>
</div>
</form>
</div>
);
}
schema.prisma
model User {
id String @default(cuid()) @id
name String?
email String? @unique
emailVerified DateTime?
image String?
password String
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
posts Post[]
@@map(name: "users")
}
If you need any other information, please let us know. Thank you in advance.
the error say that the image field is not know. Maybe you need to update the model. try "prisma db push". if you are in production you need to use prisma migrate to update the model. if you use mondoDB also you need to use push because the driver does not support migrations.