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

TypeScript+Prisma电商API带page参数时查询返回空问题排查

问题排查与解决方案

核心问题:Prisma API与Mongoose逻辑不兼容

你参考的JS项目大概率基于Mongoose,它的查询依赖链式Query对象,但Prisma的findMany()直接返回Promise,不支持链式调用find()、limit()、skip()这类方法。你的APIFeatures类完全照搬Mongoose写法,导致所有查询逻辑失效,最终返回空结果。

具体错误点

  • 构造函数传入错误对象:你把prisma.product.findMany()(一个Promise)传给APIFeatures的query参数,而Prisma根本没有可链式调用的查询构建器。后续调用this.query.find()时,Promise没有find方法,直接导致this.query变成无效值。
  • 链式调用逻辑不适配Prisma:Prisma的查询是通过给findMany()传入配置对象(包含where、skip、take等参数)实现的,而非Mongoose那样的链式拼接。

修复方案:重构APIFeatures适配Prisma

重新设计类结构,改为收集查询参数后统一执行Prisma查询:

export class APIFeatures {
  private model: any;
  private queryStr: any;
  private queryOptions: {
    where: any;
    skip?: number;
    take?: number;
  } = { where: {} };

  constructor(model: any, queryStr: any) {
    this.model = model;
    this.queryStr = queryStr;
  }

  search(): this {
    if (this.queryStr.keyword) {
      this.queryOptions.where.name = {
        contains: this.queryStr.keyword,
        mode: 'insensitive' // 对应MongoDB的$options: 'i'
      };
    }
    return this;
  }

  filter(): this {
    const queryCopy = { ...this.queryStr };
    const removeFields = ['keyword', 'limit', 'page'];
    removeFields.forEach(el => delete queryCopy[el]);

    // 转换gt/gte/lt/lte为Prisma支持的操作符格式
    Object.keys(queryCopy).forEach(key => {
      const value = queryCopy[key];
      if (typeof value === 'object') {
        Object.keys(value).forEach(operator => {
          if (['gt', 'gte', 'lt', 'lte'].includes(operator)) {
            queryCopy[key][`$${operator}`] = queryCopy[key][operator];
            delete queryCopy[key][operator];
          }
        });
      }
    });

    this.queryOptions.where = { ...this.queryOptions.where, ...queryCopy };
    return this;
  }

  pagination(resPerPage: number): this {
    const currentPage = Number(this.queryStr.page) || 1;
    this.queryOptions.skip = resPerPage * (currentPage - 1);
    this.queryOptions.take = resPerPage; // Prisma中take对应Mongoose的limit
    return this;
  }

  // 统一执行查询
  async execute(): Promise<any[]> {
    return this.model.findMany(this.queryOptions);
  }
}

修改getAllProducts方法:

async getAllProducts(req: Request, res: Response) {
  try {
    const resPerPage = 8;
    const productCount = await prisma.product.count();
    const apiFeatures = new APIFeatures(prisma.product, req.query)
      .search()
      .filter()
      .pagination(resPerPage);
    
    const response = await apiFeatures.execute(); // 必须await获取查询结果
    return res.status(200).send({ success: true, response, productCount });
  } catch (error) {
    res.status(500).send({ message: (error as Error).message });
  }
}

关键改动说明

  • 不再传递findMany()的Promise结果,直接传入Prisma模型实例。
  • 用queryOptions对象统一收集where条件、分页参数。
  • Prisma模糊查询用contains+mode: 'insensitive'替代MongoDB的$regex语法。
  • 新增execute()方法统一执行查询,必须通过await获取异步结果。

修改后访问localhost:4000/api/products/?page=1即可正常返回第一页产品数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 21:37:28