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

使用Prisma实现分页等功能时出现Route.get()回调错误求助

问题:Express路由报错,Prisma实现分页功能失败

错误信息

Error: Route.get() requires a callback function but got a [object Promise]
    at Route.<computed> [as get] (C:\Users\malay jeena\Documents\CODE\Web development\15_Pagination\pagination_v2\node_modules\express\lib\router\route.js:211:15)
    at app.<computed> [as get] (C:\Users\malay jeena\Documents\CODE\Web development\15_Pagination\pagination_v2\node_modules\express\lib\application.js:499:19)
    at Object.<anonymous> (C:\Users\malay jeena\Documents\CODE\Web development\15_Pagination\pagination_v2\app.js:66:5)

使用Prisma实现分页、搜索、排序、过滤功能时触发上述错误,保存文件后立即出现。此前用Mongoose实现相同功能无异常,已完成Prisma schema创建、迁移等基础操作,附上完整代码求解决,同时排查后续可能的TypeError。

相关代码

app.js 文件

require('dotenv').config();
const express = require('express');
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
const userController = require('./controller/userController');
const app = express();
(async () => {
  try {
    await prisma.$connect();
    console.log('Connected to the database');
    createTestData();
  } catch (error) {
    console.error('Error connecting to the database:', error);
  }
})();

const usersData = [
    { firstname: 'John', lastname: 'Doe', email: 'johndoe1@example.com', age: 25 },
    { firstname: 'Jane', lastname: 'Doe', email: 'janedoe2@example.com', age: 30 },
    { firstname: 'Alice', lastname: 'Brown', email: 'alicebrown3@example.com', age: 28 },
    { firstname: 'Bob', lastname: 'Smith', email: 'bobsmith4@example.com', age: 35 },
    { firstname: 'Charlie', lastname: 'Johnson', email: 'charliejohnson5@example.com', age: 40 },
    { firstname: 'David', lastname: 'Williams', email: 'davidwilliams6@example.com', age: 45 },
    { firstname: 'Eve', lastname: 'Jones', email: 'evejones7@example.com', age: 50 },
    { firstname: 'Frank', lastname: 'Taylor', email: 'franktaylor8@example.com', age: 55 },
    { firstname: 'Grace', lastname: 'Lee', email: 'gracelee9@example.com', age: 60 },
    { firstname: 'Hank', lastname: 'Harris', email: 'hankharris10@example.com', age: 65 },
    { firstname: 'Irene', lastname: 'Clark', email: 'ireneclark11@example.com', age: 70 },
    { firstname: 'Jack', lastname: 'Lewis', email: 'jacklewis12@example.com', age: 75 },
    { firstname: 'Kate', lastname: 'Young', email: 'kateyoung13@example.com', age: 80 },
    { firstname: 'Larry', lastname: 'Hall', email: 'larryhall14@example.com', age: 85 },
    { firstname: 'Mary', lastname: 'Allen', email: 'maryallen15@example.com', age: 90 }
];

const createTestData = async () => {
  try {
    const userCount = await prisma.user.count();

    if (userCount === 0) {
      await prisma.user.createMany({
        data: usersData.map(user => ({
          firstName: user.firstname,
          lastName: user.lastname,
          email: user.email,
          age: user.age,
        })),
      });
      console.log('Test data created successfully');
    } else {
      console.log('Test data already exists');
    }
  } catch (error) {
    console.error('Error creating test data:', error);
  }
};

app.get('/users', userController.getUsers, userController.getAllUsers);

app.listen(3000, () => {
  console.log("Server is running on port 3000");
});

pagination.js 文件

const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();

async function getUsersWithPagination(modelName) {
  return async (req, res, next) => {
    const page = parseInt(req.query.page) || 1;
    const limit = parseInt(req.query.limit) || 5;
    const search = req.query.search || "";
    const sortBy = req.query.sortBy || 'firstname';
    const sortOrder = parseInt(req.query.sortOrder) || 1;
    const filterBy = req.query.filterBy ? req.query.filterBy.split(',') : [];
    const filterValue = req.query.filterValue || '';

    const startIndex = (page - 1) * limit;
    const endIndex = page * limit;

    const results = {};

    if (endIndex < await modelName.count()) {
      results.next = {
        page: page + 1,
        limit: limit
      };
    }

    if (startIndex > 0) {
      results.previous = {
        page: page - 1,
        limit: limit
      };
    }

    // Add search functionality
    const searchQuery = {
      OR: [
        { firstname: { contains: search, mode: 'insensitive' } },
        { lastname: { contains: search, mode: 'insensitive' } }
      ]
    };

    // Add filtering functionality
    const filter = {};
    if (filterBy.length > 1 && filterValue) {
      const filterConditions = filterBy.map(field => {
        return { [field]: { contains: filterValue, mode: 'insensitive' } };
      });
      filter.OR = filterConditions;
    } else if (filterBy && filterValue) {
      filter[filterBy] = isNaN(Number(filterValue)) ? filterValue : Number(filterValue);
    }

    // Add sorting functionality
    const orderBy = {
      [sortBy]: sortOrder === 1 ? 'asc' : 'desc'
    };

    try {
      results.results = await modelName.findMany({
        where: { ...searchQuery, ...filter },
        orderBy,
        take: limit,
        skip: startIndex
      });
      console.log(results);
      res.paginatedResults = results;
      res.next = results.next;
      res.previous = results.previous;
      res.users = results.results.length;
      next();
    } catch (error) {
      res.status(500).json({ message: error.message });
    }
  };
}

module.exports = getUsersWithPagination;

userController.js 文件

const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
const getUsersWithPagination = require('../util/pagination');

const getUsers = getUsersWithPagination(prisma.user);

const getAllUsers = async (req, res) => {
  try {
    const users = res.paginatedResults.results || [];
    res.status(200).json({
      status: 'success',
      results: res.users,
      next: res.next,
      previous: res.previous,
      data: {
        users
      }
    });
  } catch (error) {
    res.status(400).json({
      status: 'fail',
      message: error
    });
  }
};

module.exports = {
  getAllUsers,
  getUsers
};

解决方案

1. 修复路由回调类型错误

核心问题是getUsersWithPagination被定义为async函数,导致它返回Promise而非Express中间件函数。Express路由需要普通函数作为回调,因此去掉外层async:

// pagination.js 中修改函数定义
function getUsersWithPagination(modelName) {
  return async (req, res, next) => {
    // 原有逻辑不变
  };
}

2. 统一Prisma字段名

createTestData中用firstName/lastName(Prisma默认驼峰命名),但pagination.js中用firstname/lastname,会导致查询报错,统一为驼峰字段:

// pagination.js 中修改搜索和排序逻辑
const searchQuery = {
  OR: [
    { firstName: { contains: search, mode: 'insensitive' } },
    { lastName: { contains: search, mode: 'insensitive' } }
  ]
};

const sortBy = req.query.sortBy || 'firstName';

3. 修复分页count的异常捕获

原代码中await modelName.count()未包裹在try块,失败会导致未捕获异常,将分页判断移到try块内:

// pagination.js 中修改try块内容
try {
  const totalCount = await modelName.count({ where: { ...searchQuery, ...filter } });
  
  if (endIndex < totalCount) {
    results.next = {
      page: page + 1,
      limit: limit
    };
  }

  if (startIndex > 0) {
    results.previous = {
      page: page - 1,
      limit: limit
    };
  }

  results.results = await modelName.findMany({
    where: { ...searchQuery, ...filter },
    orderBy,
    take: limit,
    skip: startIndex
  });
  // 后续赋值逻辑不变
} catch (error) {
  res.status(500).json({ message: error.message });
}

4. 修复过滤逻辑的字段处理

原代码中filterBy是数组,单个字段时直接用数组作为键名会出错,且数字字段(如age)不能用contains操作:

// pagination.js 中修改过滤逻辑
const filter = {};
if (filterBy.length > 0 && filterValue) {
  if (filterBy.length > 1) {
    const filterConditions = filterBy.map(field => {
      if (field === 'age' && !isNaN(Number(filterValue))) {
        return { [field]: { equals: Number(filterValue) } };
      }
      return { [field]: { contains: filterValue, mode: 'insensitive' } };
    });
    filter.OR = filterConditions;
  } else {
    const field = filterBy[0];
    if (field === 'age' && !isNaN(Number(filterValue))) {
      filter[field] = Number(filterValue);
    } else {
      filter[field] = { contains: filterValue, mode: 'insensitive' };
    }
  }
}

5. 复用Prisma实例

多个文件重复创建PrismaClient可能导致连接泄漏,新建prisma.js统一导出实例:

// prisma.js
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();

module.exports = prisma;

在app.js、pagination.js、userController.js中替换引入:

const prisma = require('./prisma');

后续验证

完成修改后启动服务器,测试以下场景:

  1. 基础分页:http://localhost:3000/users
  2. 搜索:http://localhost:3000/users?search=John
  3. 排序:http://localhost:3000/users?sortBy=age&sortOrder=-1
  4. 过滤:http://localhost:3000/users?filterBy=age&filterValue=25

内容的提问来源于stack exchange,提问作者Malay Jeena

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 22:17:02