NextJS13 + Prisma数据获取报500错误,无法映射地区与城市数据
修复NextJS13 + Prisma 500错误的步骤
1. 修正API路由(核心问题)
你的api/regions路由存在两个关键错误:
- 错误返回
req而非数据响应 - 错误使用
NextApiResponse(App Router下应使用NextResponse)
修复后的App Router API路由
import { NextResponse } from 'next/server'; import prismadb from '@/lib/prismadb'; export async function GET() { try { const regions = await prismadb.region.findMany({ include: { villes: true }, }); return NextResponse.json(regions); } catch (error) { console.error('Error fetching regions:', error); return NextResponse.json({ error: 'Failed to fetch regions' }, { status: 500 }); } }
若使用Pages Router,API路由应改为
import type { NextApiRequest, NextApiResponse } from 'next'; import prismadb from '@/lib/prismadb'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { if (req.method !== 'GET') { return res.status(405).json({ error: 'Method not allowed' }); } try { const regions = await prismadb.region.findMany({ include: { villes: true }, }); res.status(200).json(regions); } catch (error) { console.error('Error fetching regions:', error); res.status(500).json({ error: 'Failed to fetch regions' }); } }
2. 优化组件数据获取逻辑
方案一:App Router(推荐)
NextJS13 App Router不支持getServerSideProps,需拆分组件:
- 用Server Component直接调用Prisma获取数据
- 用Client Component处理交互状态
Server Component(页面/父组件)
import CandidatureClient from './CandidatureClient'; import prismadb from '@/lib/prismadb'; async function CandidaturePage() { const regions = await prismadb.region.findMany({ include: { villes: true }, }); return <CandidatureClient regions={regions} />; } export default CandidaturePage;
Client Component(交互逻辑)
去掉冗余的villes props,修复map的key和图片路径:
'use client'; import React, { useState } from "react"; import Link from "next/link"; import Image from "next/image"; interface Ville { id: number; name: string; startDate: string; endDate: string; programStartDate: string; link: string; } interface Region { id: number; name: string; villes: Ville[]; } interface Props { regions: Region[]; } function toClassName(name: string) { return name.replace(/ /g, "_"); } const CandidatureClient: React.FC<Props> = ({ regions }) => { const [stage, setStage] = useState<"REGION" | "VILLE" | "JOIN">("REGION"); const [selectedRegion, setSelectedRegion] = useState<Region | null>(null); const [selectedVille, setSelectedVille] = useState<Ville | null>(null); const selectRegion = (region: Region) => { setSelectedRegion(region); setStage("VILLE"); }; const selectVille = (ville: Ville) => { setSelectedVille(ville); setStage("JOIN"); }; return ( <section className={`Section-Candidature ${stage}`}> <div className="Choice"> <button className={stage === "REGION" ? "active" : ""}>CHOISIS TA REGION</button> <button className={stage === "VILLE" ? "active" : ""}>CHOISIS TA VILLE</button> <button className={stage === "JOIN" ? "active" : ""}>REJOINS NOUS!</button> </div> {stage === "REGION" && ( <div className="FrancePays"> {regions.map((region) => ( <div key={region.id} className={toClassName(region.name)}> <p>{region.name}</p> <Image src="/SVG/flecheBlanche.svg" alt="Flèche blanche" width={40} height={40} className="Fleches" onClick={() => selectRegion(region)} /> </div> ))} </div> )} {stage === "VILLE" && selectedRegion && ( <div className="FranceRegion"> {selectedRegion.villes.map((ville) => ( <div key={ville.id} className={toClassName(ville.name)}> <p>{ville.name}</p> <Image src="/SVG/flecheBlanche.svg" alt="Flèche blanche" width={40} height={40} className="Fleches" onClick={() => selectVille(ville)} /> </div> ))} </div> )} {stage === "JOIN" && selectedVille && ( <div className="Join"> <div className="TroisSemaines"> <p>3</p> <p>SEMAINES DE FORMATION INTENSIVE</p> </div> <div className="CinqMois"> <p>5</p> <p>MOIS D'ACCOMPAGNEMENT</p> </div> <div className="Annee"> <p>DECOUVRE LA PROMO 2023</p> <Image src='/assets/Annee.png' alt='Année' fill objectFit='cover' placeholder="blur" blurDataURL={'/assets/Annee.png'} className='Determines' /> </div> <div className="Debut"> <p>{selectedVille.startDate}</p> <p>DEBUT APPEL CANDIDATURE</p> </div> <div className="Fin"> <p>{selectedVille.endDate}</p> <p>FIN APPEL CANDIDATURE</p> </div> <div className="Formation"> <p>{selectedVille.programStartDate}</p> <p>FORMATION INTENSIVE DE 6 MOIS</p> </div> <div className="Lien"> <p>JE POSTULE</p> <Link href={selectedVille.link} target="_blank" > <Image src='/SVG/flecheBlanche.svg' alt='Flèche blanche' fill objectFit='contain' placeholder="blur" blurDataURL={'/SVG/flecheBlanche.svg'} className='Fleches' /> </Link> </div> </div> )} </section> ); }; export default CandidatureClient;
方案二:Pages Router
直接在getServerSideProps中调用Prisma,避免本地API请求:
// 组件代码保留,修改getServerSideProps export const getServerSideProps = async () => { try { const regions = await prismadb.region.findMany({ include: { villes: true }, }); return { props: { regions, }, }; } catch (error) { console.error("Error fetching data:", error); return { props: { regions: [], }, }; } };
3. 额外检查项
- 确保
prismadb初始化正确,采用单例模式避免重复实例化 - 验证
.env文件中DATABASE_URL数据库连接字符串配置正确 - 运行
npx prisma generate更新Prisma客户端 - 运行
npx prisma db push或npx prisma migrate deploy同步数据库schema
内容的提问来源于stack exchange,提问作者SamCoding
相关产品推荐
相关产品推荐

