Search code examples
reactjstypescriptnext.jsprismavercel

When deploying a web app created with typescript (next.js) to vercel, I get a build error and cannot deploy


▼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

  • pages/api/user/index.tsx
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

// 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.


Solution

  • 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.