You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript开发的Next.js应用部署Vercel时构建失败求助

Vercel构建失败:Prisma UserCreateInput类型不匹配错误

环境信息

依赖版本(取自package.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",
  "typescript": "4.5.5"
}

技术栈

  • 框架:Next.js
  • 数据库:Supabase
  • 图片存储:Supabase Storage
  • ORM:Prisma
  • 表单:react-hook-form
  • 开发语言:TypeScript

问题现象

本地执行npm run dev、npm run build、npm run start均正常,用户注册、更新及图片获取功能无异常。但仅在Vercel构建时出现类型错误,导致部署失败。数据库中存储图片的字段类型为string,推测是Next.js与Prisma的类型定义不匹配,但无法定位具体原因。

Vercel构建错误日志

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

相关代码

用户注册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);
  }
}

用户注册组件(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 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
    const url = await supabase.storage
      .from("photos")
      .getPublicUrl(JSON.stringify(data));
    const { publicUrl } = url.data;

    // 将获取到的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;
      // 使用表单输入的密码,避免使用已哈希的密码
      const password = formData.password;
      // 直接通过signIn登录

      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>
  );
}

Prisma Schema(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")
}

内容的提问来源于stack exchange,提问作者user21514407

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 05:55:10