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

如何用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 17:32:14