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

将Node.js Express后端部署到Vercel时遇404/500错误求助

Node.js Express部署Vercel遇404/500错误排查与修复

尝试将Node.js Express后端部署至Vercel平台时,持续出现404或500错误。项目根目录下有handlers文件夹,内部包含多个路由文件夹及index.js文件,该文件汇总所有路由逻辑并传递给根目录的index.js。以下是项目代码示例、当前配置及错误情况:


代码示例

handler/*.js 示例

const { exec } = require("child_process");
const { promisify } = require("util");
/**
 * @param {*} event
 * @param {*} models
 * @returns
 */

module.exports.handler = async function (event, models) {

    const execPromise = promisify(exec);

    const pythonReturn = await execPromise("python3 app.py");
  
    const result = JSON.parse(pythonReturn.stdout)
  
      return {
        statusCode: 200,
        body: JSON.stringify(result),
      };
  
  };
  
  module.exports.path = "/getSurebets";
  module.exports.method = "GET";

server.js

'use strict'

const express = require('express')
const bodyParser = require('body-parser')
const cors = require('cors')
const app = express()
const router = express.Router()
const aliveTime = new Date().getTime()
const PORT = process.env.PORT || 3002; 
const Handler = require("./index")

router.use(cors())
router.use(bodyParser.json())
router.use(bodyParser.urlencoded({ extended: true }))

router.get('/', (req, res) => {
  req.json({ aliveTime })
})

router.use(async function(req, res){
  const event = {
    "resource": req.path,
    "path": req.path,
    "httpMethod": req.method,
    "headers": req.headers,
    "queryStringParameters": req.query,
    "pathParameters": req.params,
    "stageVariables": null,
    "body": JSON.stringify(req.body),
    "isBase64Encoded": false,
    "requestContext": {
      "authorizer": {}
    }
  }

  try{
    const response = await Handler.handler(event)
    res
      .set(response.headers)
      .status(response.statusCode)
    
    const data = response.body
      
    if(req.path.indexOf("render") < 0){
      res.send(JSON.parse(data))
    }else{
      res.send(data)
    }

  }catch(e){
    console.error(e)
    res.status(500).send(e)
  }
})

app.use('/v1/', router)
app.listen(PORT)

console.log(`Listen on http://0.0.0.0:${PORT}`)
module.exports = app

根目录 index.js

const fns = require("./handlers")
const cors = require('./lib/cors')
const models = require("./models");


const extractPathValues = (pathExpression, httpPath) => {
  const pathExpressionPattern = pathExpression.replace(/{[\w]+}|:[\w]+/g, '([^/]+)')
  const pathValueRegex = new RegExp(`^${pathExpressionPattern}$`)
  const pathValues = pathValueRegex.exec(httpPath)
  return pathValues && pathValues.length > 0 ? pathValues.slice(1) : null
}

const extractPathNames = (pathExpression) => {
  const pathExpressionPattern = pathExpression?.replace(/{[\w.]+}|:[\w.]+/g, '[:{]([\\w]+)}?');
  const pathNameRegex = new RegExp(`^${pathExpressionPattern}$`)
  const pathNames = pathNameRegex.exec(pathExpression)
  return pathNames && pathNames.length > 0 ? pathNames.slice(1) : null
}

const getRouterHandler = (event) => {
  const routesByMethod = fns.filter(r => r.method === event.httpMethod)
  for(const route of routesByMethod) {
    const pathPartNames = extractPathNames(route.path)
    const pathValues = extractPathValues(route.path, event.path)
    if (pathValues && pathPartNames) {
      const params = {}
      let keyIndex = 0;
      for(const key of pathPartNames) {
        params[ key ] = pathValues[ keyIndex ]
        keyIndex++;
      }
      return {route, params}
    }
  }
}

const getHandler = async (event) => {
  return getRouterHandler(event)
}

exports.handler = async (event, context) => {
  if(event.Records) return tRender(event, context, models)
  if(process.env.NODE_ENV!=='test') console.log(event)
  const HANDLER = await getHandler(event)
  if(!HANDLER) return { statusCode: 404, body: JSON.stringify({ message: 'Not Found' }) }
  const { route, params } = HANDLER
  if(!event.pathParameters || !Object.keys(event.pathParameters).length) {
    event.pathParameters = params
  }
  
  if((process.env.NODE_ENV || '').trim() !== 'test') console.debug({ event }) 

  const response = cors(
    await route.handler(event, models, context).catch(err => {
      console.error(err)
      if(!err.statusCode || !err.body ) {
        return Promise.reject(err)
      }
      return Promise.resolve(err)
    })
  )
  return response
}

当前 vercel.config

{
    "version": 2,
    "name": "omnes-api",
    "builds": [
       { "src": "index.js", "use": "@vercel/node" }
    ],
    "routes": [
        {
          "src": "handlers/index.js",
          "dest": "index.js"
        }
      ]
 }

错误情况

错误截图


修复方案

1. 修正Vercel路由配置

当前路由规则仅匹配handlers/index.js路径,无法转发所有API请求。修改为匹配所有路径并指向根目录处理函数:

{
    "version": 2,
    "name": "omnes-api",
    "builds": [
       { "src": "index.js", "use": "@vercel/node" }
    ],
    "routes": [
        {
          "src": "/(.*)",
          "dest": "/index.js"
        }
      ]
 }

2. 适配Express路径前缀

由于Express中使用了/v1/前缀,需在路由匹配逻辑中去除前缀,确保与handler中定义的路径匹配:

const getRouterHandler = (event) => {
  // 去除/v1/前缀,匹配handler中定义的path
  const strippedPath = event.path.replace(/^\/v1/, '');
  const routesByMethod = fns.filter(r => r.method === event.httpMethod)
  for(const route of routesByMethod) {
    const pathPartNames = extractPathNames(route.path)
    const pathValues = extractPathValues(route.path, strippedPath)
    if (pathValues && pathPartNames) {
      const params = {}
      let keyIndex = 0;
      for(const key of pathPartNames) {
        params[ key ] = pathValues[ keyIndex ]
        keyIndex++;
      }
      return {route, params}
    }
  }
}

3. 解决Python运行环境问题(500错误可能原因)

Vercel Node环境默认无Python,需添加Python构建支持或替换为Node实现:

{
    "version": 2,
    "name": "omnes-api",
    "builds": [
       { "src": "index.js", "use": "@vercel/node" },
       { "src": "app.py", "use": "@vercel/python" }
    ],
    "routes": [
        {
          "src": "/(.*)",
          "dest": "/index.js"
        }
      ]
 }

4. 本地验证

部署前用Vercel CLI测试:

vercel dev

访问http://localhost:3000/v1/getSurebets确认响应正常后再部署。


内容的提问来源于stack exchange,提问作者Daniel Alves

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 08:40:28