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

Next.js13保存图片时API路由返回500错误(Prisma+Zod)

问题

我正在开发一个Next.js应用,用户可生成Logo并保存至账户。点击“Save Image”按钮时,会向API路由发送POST请求,将图片URL和用户ID保存到Prisma数据库。

目前遇到的问题是:点击按钮后浏览器控制台出现500错误,图片无法保存。我检查了API路由实现和Prisma schema,但未找到错误原因。

前端代码(DisplayLogo组件)

'use client';

import { PageWrapper } from '@/components/Page-Wrapper';
import Image from 'next/image';
import { useRouter, useSearchParams } from 'next/navigation';
import React, { useEffect, useState } from 'react';
import { useMutation } from '@tanstack/react-query';
import axios from 'axios';
import { toast } from '@/hooks/use-toast';
import { Button } from '@/components/ui/button';

const DisplayLogo: React.FC = () => {
  const [image, setImage] = useState<string | null>(null);
  const searchParams = useSearchParams();
  const router = useRouter();

  useEffect(() => {
    const imageQueryParam = searchParams?.get('image');
    if (typeof imageQueryParam === 'string') {
      setImage(imageQueryParam);
    }
  }, [searchParams]);

  const createImageMutation = useMutation(
    async ({ imageUrl }: { imageUrl: string }) => {
      await axios.post('/api/save-image', { imageUrl });
    },
    {
      onError: () => {
        toast({
          title: 'Something went wrong',
          description: 'Could not save image. Please try again later.',
          variant: 'destructive',
        });
      },
      onSuccess: () => {
        toast({
          title: 'Image saved',
          description: 'Your image has been saved to your account',
          variant: 'default',
        });
      },
    }
  );

  const handleSaveImage = () => {
    if (image) {
      createImageMutation.mutate({ imageUrl: image });
    }
  };

  return (
    <PageWrapper>
      <main className='py-40 px-8 md:px-12 lg:px-24 flex flex-col items-center justify-center'>
        <h1 className='text-3xl md:text-4xl lg:text-5xl font-bold mb-4'>
          Your Generated Image
        </h1>

        {image ? (
          <div className='mt-8'>
            <Image
              className='w-64 h-64 object-cover rounded-md shadow-lg'
              src={image}
              alt='AI Generated'
              width={500}
              height={500}
            />
          </div>
        ) : (
          <p className='text-center text-lg mx-auto max-w-md mb-8'>
            No logo image found. Please go back and generate an image first.
          </p>
        )}

        <div className='flex flex-col items-center mt-5'>
          {image && <Button onClick={handleSaveImage}>Save Image</Button>}

          <p className='text-center text-lg mx-auto max-w-md mt-5'>
            Congratulations! Your ai generated has been generated using the input you
            provided!
          </p>
        </div>
      </main>
    </PageWrapper>
  );
};

export default DisplayLogo;

API路由代码(/api/save-image)

// Import the required modules and types
import { db } from '@/lib/db';
import { getAuthSession } from '@/lib/auth';
import { ImageValidator } from '@/lib/validators/images';
import { z } from 'zod';

export async function POST(req: Request) {
  try {
    const session = await getAuthSession();
    if (!session?.user) {
      return new Response('Unauthorized', { status: 401 });
    }

    const body = await req.json();
    const { imageUrl } = ImageValidator.parse(body);

    // Check if the image already exists
    const image = await db.image.findUnique({
      where: {
       id: imageUrl,
      },
    });
    if (image) {
      return new Response('Image already exists', { status: 409 });
    }

    // Create the image and associate it with the user
    const newImage = await db.image.create({
      data: {
        userId: session.user.id,
        url: imageUrl,
        createdAt: new Date(),
      },
    });

    return new Response(JSON.stringify({ message: 'Image saved successfully' }), {
      status: 201,
      headers: { 'Content-Type': 'application/json' },
    });
  } catch (error) {
    if (error instanceof z.ZodError) {
      return new Response(error.message, { status: 422 });
    }
    return new Response('Something went wrong', { status: 500 });
  }
}

Zod验证Schema

import { z } from 'zod';

export const ImageValidator = z.object({
  imageUrl: z.string().url(),
  userId: z.string().optional(),
  createdAt: z.date().optional(),
  updatedAt: z.date().optional(),
});

export type ImageData = z.infer<typeof ImageValidator>;

错误截图

Network Error
xhr.js


排查与解决建议
  • 核心问题:存在性查询条件错误
    API中用db.image.findUnique({ where: { id: imageUrl } }),但Prisma模型的id通常是UUID或自增ID,不是图片URL。这会导致数据库查询失败,抛出500错误。
    解决:修改为按url字段查询:

    const image = await db.image.findUnique({
      where: {
        url: imageUrl,
      },
    });
    

    同时确保Prisma Schema中Image模型的url字段设置了unique: true,避免重复保存。

  • 补充错误日志定位问题
    当前代码只捕获Zod错误,其他数据库、会话相关错误直接返回500,无法看到具体原因。在catch块添加日志打印:

    catch (error) {
      if (error instanceof z.ZodError) {
        return new Response(error.message, { status: 422 });
      }
      console.error('Save image error:', error); // 打印到服务器控制台
      return new Response('Something went wrong', { status: 500 });
    }
    

    这样可以在本地开发服务器或Vercel控制台看到具体错误信息,比如Prisma字段不匹配、数据库连接问题等。

  • 优化前端错误提示
    前端可以根据响应状态码区分错误类型,给用户更精准提示:

    onError: (error) => {
      if (error.response?.status === 422) {
        toast({
          title: '无效URL',
          description: '图片URL格式不正确,请检查后重试。',
          variant: 'destructive',
        });
      } else if (error.response?.status === 409) {
        toast({
          title: '图片已存在',
          description: '该图片已经保存到你的账户中。',
          variant: 'destructive',
        });
      } else {
        toast({
          title: '保存失败',
          description: '图片保存失败,请稍后重试。',
          variant: 'destructive',
        });
      }
    },
    
  • 检查Prisma Schema匹配性
    确认Image模型字段与API中创建数据的字段一致,示例Schema:

    model Image {
      id        String   @id @default(uuid())
      url       String   @unique
      userId    String
      user      User     @relation(fields: [userId], references: [id])
      createdAt DateTime @default(now())
      updatedAt DateTime @updatedAt
    }
    

    若createdAt已设默认值,API中无需手动传递createdAt: new Date(),由数据库自动生成即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 00:11:08