如何用Prisma在MongoDB中查询唯一商品?API路由报错求助
Next.js 13.4 + Prisma + MongoDB 单个商品API路由错误修复
我在用Next.js 13.4、MongoDB和Prisma搭建全栈电商网站时,尝试获取单个商品的API路由出现错误,以下是相关信息及修复方案:
相关代码与错误信息
Prisma Schema 中的Product模型
model Product { id String @id @default(auto()) @map("_id") @db.ObjectId name String description String price Float hasSale Boolean? @default(false) salePrice Float? saleUntil DateTime? image String? stock Int? @default(10) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt likes Like[] @relation("ProductLikes") cart Cart[] @relation("ProductCart") category Category @relation(fields: [categoryId], references: [id]) categoryId String @db.ObjectId }
当前API路由代码(app/api/products/route.ts)
import prisma from "@/app/libs/prismadb"; import { NextResponse, NextRequest } from "next/server"; import { ObjectId } from "mongodb"; export async function GET(request: Request) { const productId = request.url.split("/")[3]; let products; try { if (productId !== null) { products = await prisma.product.findUnique({ where: { id: productId, }, include: { category: true, }, }); } else { products = await prisma.product.findMany({ include: { category: true, }, }); } console.log(request.url); return NextResponse.json(products); } catch (error) { console.log(error, "FETCH PRODUCTS ERROR"); return new NextResponse("Something went wrong", { status: 500 }); } }
错误信息
Inconsistent column data: Malformed ObjectID: provided hex string representation must be exactly 12 bytes, instead got: "api", length 3.
中文翻译:数据列不一致:格式错误的ObjectID:要求传入的十六进制字符串必须恰好为12字节,实际得到的是"api",长度仅3字节。
错误原因
问题出在request.url.split("/")[3]的路径拆分逻辑:
- 当访问商品列表接口(如
http://localhost:3000/api/products)时,URL拆分后的数组为['http:', '', 'localhost:3000', 'api', 'products'],此时取索引3得到的是"api",而非商品ID。 - Prisma中Product的
id字段被标记为@db.ObjectId,要求传入的字符串必须是12字节的合法MongoDB ObjectId格式,"api"显然不符合要求,因此触发格式错误。
修复方案
方案1:使用Next.js动态路由(推荐,符合RESTful规范)
创建动态路由文件app/api/products/[productId]/route.ts,通过params直接获取商品ID,同时将列表接口保留在原路由:
单个商品路由(app/api/products/[productId]/route.ts)
import prisma from "@/app/libs/prismadb"; import { NextResponse } from "next/server"; import { ObjectId } from "mongodb"; export async function GET(request: Request, { params }: { params: { productId: string } }) { const { productId } = params; // 先验证ObjectId合法性,避免无效值传入Prisma if (!ObjectId.isValid(productId)) { return new NextResponse("无效的商品ID", { status: 400 }); } try { const product = await prisma.product.findUnique({ where: { id: productId }, include: { category: true } }); if (!product) { return new NextResponse("商品不存在", { status: 404 }); } return NextResponse.json(product); } catch (error) { console.log(error, "FETCH PRODUCT ERROR"); return new NextResponse("服务器内部错误", { status: 500 }); } }
商品列表路由(app/api/products/route.ts)
import prisma from "@/app/libs/prismadb"; import { NextResponse } from "next/server"; export async function GET() { try { const products = await prisma.product.findMany({ include: { category: true } }); return NextResponse.json(products); } catch (error) { console.log(error, "FETCH PRODUCTS ERROR"); return new NextResponse("服务器内部错误", { status: 500 }); } }
方案2:同一路由处理列表与单个商品(不推荐)
若需在同一个路由中兼容两种场景,可通过查询参数传递商品ID:
import prisma from "@/app/libs/prismadb"; import { NextResponse } from "next/server"; import { ObjectId } from "mongodb"; export async function GET(request: Request) { // 从查询参数获取productId,例如请求地址:/api/products?id=xxx const url = new URL(request.url); const productId = url.searchParams.get("id"); let products; try { if (productId) { if (!ObjectId.isValid(productId)) { return new NextResponse("无效的商品ID", { status: 400 }); } products = await prisma.product.findUnique({ where: { id: productId }, include: { category: true } }); } else { products = await prisma.product.findMany({ include: { category: true } }); } return NextResponse.json(products); } catch (error) { console.log(error, "FETCH PRODUCTS ERROR"); return new NextResponse("服务器内部错误", { status: 500 }); } }
额外注意事项
- 无论采用哪种方案,都必须先验证ObjectId的合法性,避免无效值传入Prisma导致错误。
- 动态路由更符合RESTful API设计规范,便于维护和扩展,优先推荐使用。
内容的提问来源于stack exchange,提问作者sellimenes
相关产品推荐
相关产品推荐

