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

Node v16.6.0环境下Nodemon提示应用崩溃,求排查方案

问题描述

我的环境:Node.js v16.6.0、NPM v8.5.0
运行项目时Nodemon提示:Nodemon: app crashed waiting for file changes before starting
已尝试通过Windows任务管理器结束Node.js JavaScript Runtime进程,但问题未解决。

以下是server.js代码:

import * as express from 'express';
import * as bodyParser from 'body-parser';
import 'reflect-metadata';
import * as cors from 'cors';
import * as swaggerUi from 'swagger-ui-express';
import * as swaggerJSDoc from 'swagger-jsdoc';
import * as lodash from 'lodash';
import * as path from 'path';
import * as fs from 'fs';
import * as logger from 'morgan';
import * as dotenv from 'dotenv';
import * as http from 'http';
import * as AgendaInstance from 'agenda';
import * as AgendashInstance from 'agendash';

import initGlobalVariables from './constants';
import HttpResponse from './services/response';
import CustomResponse from './services/response/customResponse';
import * as logrotate from 'logrotator';
import Agenda from './services/agenda';
// Import passport strategies
import passportStrategies from './config/passport';
import {helpers} from './utils';

/** Rotation is based on copying the file contents and then truncating the file size to 0.
 *  This way we avoid file renaming problems where programs are not always prepared to handle a log file being renamed.
 */
const rotator = logrotate.rotator;

// Load environment variables from .env file
dotenv.config();

(async () => {
    // Init global variables
    await initGlobalVariables();

    // Create Express server
    const app = express();

    // Express configuration
    app.all('/*', function (req, res, next) {
        res.header('Access-Control-Allow-Origin', '*');
        next();
    });

    // Request body parser
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({extended: true}));

    // Enable CORS
    app.use(cors());

    // Middleware to check tenant code
    // app.use(async (req, res, next) => {
    //     let tenantCode = req.get('code');
    //     if (tenantCode) {
    //         // Update current connection by the new one
    //         // NOTE THAT SWITCHING TENANT DB WILL AFFECT TO ALL REQUEST FROM NOW ON
    //         await global.mongoInstances['default'].updateCurrentConnectionByTenantCode(
    //             tenantCode, global.connections['default']);

    //         // Re init model base on the new connection
    //         global.mongoModel = initModel(global.connections.currentTenantConnection);
    //     }

    //     next();

    // });

    // Import public routers
    const initPublicRouter = await import('./routes');
    initPublicRouter(app);

    // Set template engine
    app.set('views', 'src/views');
    app.set('view engine', 'hbs');

    /*///////////////////////////////////////////////////////////////
      /////                   START LOGS CONFIG                  /////
      ///////////////////////////////////////////////////////////////*/

    // For production, access logs keep all logs about http requests, including error logs
    if (process.env.NODE_ENV === 'development') {
        // Create log directory
        const logDirectory = path.resolve('logs');
        fs.existsSync(logDirectory) || fs.mkdirSync(logDirectory);

        // Define directory to write access logs
        const logPath = path.join(process.cwd(), '/logs/access.log');
        const accessLogStream = fs.createWriteStream(logPath, {flags: 'a'});
        app.use(logger('combined', {stream: accessLogStream}));

        // Check file rotation every 30 minutes, and rotate the file if its size exceeds 10 mb.
        // Keep only 3 rotated files and compress (gzip) them.
        rotator.register(logPath, {schedule: '30m', size: '10m', compress: true, count: 3});
        rotator.on('error', function (err) {
            logger('Oops, an error occured!');
        });

        // 'rotate' event is invoked whenever a registered file gets rotated
        rotator.on('rotate', function (file) {
            logger('File ' + file + ' was rotated!');
        });
    }

    // Show method, url, status , response time, res content length in console log
    app.use(logger('dev'));

    /*///////////////////////////////////////////////////////////////
      /////                    END LOGS CONFIG                  /////
      ///////////////////////////////////////////////////////////////*/

    /*///////////////////////////////////////////////////////////////
      /////                 START SWAGGER CONFIG                  /////
      ///////////////////////////////////////////////////////////////*/

    const swaggerDefinition = {
        info: {
            // API informations (required)
            title: 'Home Key APIs', // Title (required)
            version: '1.0.0', // Version (required)
        },
        basePath: '/api', // Base path (optional),
        securityDefinitions: {
            // Enable api key in header, using security: - auth: [] in jsdoc to enable check this
            auth: {
                type: 'apiKey',
                in: 'header',
                name: 'Authorization',
            },
        },
    };

    const filenameExtension = process.env.NODE_ENV === 'local' ? 'ts' : 'js';
    const basePath = process.env.NODE_ENV === 'local' ? './src/' : './build/';
    const lookupFolders = [path.join(basePath, 'controllers'), path.join(basePath, 'models')];

    let swaggerApis = [];

    //General path to lookup swagger define
    lodash.forEach(lookupFolders, lookupFolder => {
        const directories = helpers.getDirectories(lookupFolder);

        lodash.forEach(directories, directory => {
            swaggerApis.push(path.join(directory, `*.${filenameExtension}`));
        });
    });

    let options = {
        swaggerDefinition,
        apis: swaggerApis, // <-- not in the definition, but in the options
    };

    const swaggerSpec = swaggerJSDoc(options);

    app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));

    /*///////////////////////////////////////////////////////////////
      /////                  END SWAGGER CONFIG                  /////
      ///////////////////////////////////////////////////////////////*/

    /*///////////////////////////////////////////////////////////////
      /////             START AGENDA DASHBOARD CONFIG             /////
      ///////////////////////////////////////////////////////////////*/

    // Init agenda instance
    const agenda = new AgendaInstance({
        db: {
            address: global.configs.agenda.dbUri,
            collection: 'scheduleJobs',
            options: {
                useNewUrlParser: true,
            },
        },
    });
    // Serve agenda dashboard
    app.use('/agenda/dashboard', AgendashInstance(agenda));

    /*///////////////////////////////////////////////////////////////
      /////               END AGENDA DASHBOARD CONFIG             /////
      ///////////////////////////////////////////////////////////////*/

    // Init Passport
    passportStrategies(app);

    /*///////////////////////////////////////////////////////////////
      /////                 START CORS MIDDLEWARE                 /////
      ///////////////////////////////////////////////////////////////*/

    app.use(function (req, res, next) {
        let value = req.get('referer');

        if (!value) {
            value = req.get('origin');
        }

        res.header('Access-Control-Allow-Credentials', 'true');
        res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
        res.header(
            'Access-Control-Allow-Headers',
            'Origin, Content-Type, Authorization, REST-API-KEY-HEADER, x-auth-token, switch-company-code',
        );

        // intercept OPTIONS method
        if ('OPTIONS' === req.method) {
            res.sendStatus(200);
        } else {
            next();
        }
    });

    /*///////////////////////////////////////////////////////////////
      /////                 END CORS MIDDLEWARE                 /////
      ///////////////////////////////////////////////////////////////*/

    /*///////////////////////////////////////////////////////////////
      /////                   START STATIC PATH                  /////
      ///////////////////////////////////////////////////////////////*/

    app.use(express.static(path.resolve('public')));
    app.use(express.static(path.resolve('src/views')));

    app.use('/images/', express.static(path.resolve('public/images')));

    // Test socket client
    app.get('/socketClient', function (req, res) {
        res.sendFile(path.join(process.cwd(), '/public/socketClient.html'));
    });

    /*///////////////////////////////////////////////////////////////
      /////                    END STATIC PATH                   /////
      ///////////////////////////////////////////////////////////////*/

    // Catch 404 and forward to error handler
    app.use(function (req, res, next) {
        // Normalize error format
        let error = HttpResponse.returnErrorWithMessage(`Not found: ${req.method} ${req.originalUrl}`);
        // Set error status code
        res.status(404);
        // Pass to the next middleware
        next(error);
    });

    // Step 1:  Write error logs file in production mode
    if (process.env.NODE_ENV === 'development') {
        app.use((err, req, res, next) => {
            next(err);
        });
    }

    // Step 2: Return meaningful error, so there is no stack traces leaked to user
    app.use(function (err, req, res, next) {
        if (err instanceof CustomResponse && res.statusCode !== null) {
            res.json(err);
        } else {
            // Set error status code
            res.status(500);
            HttpResponse.returnInternalServerResponseWithMessage(res, err.message);
        }
    });

    // Create http server with express
    const httpServer = http.createServer(app);

    // Start Express server
    httpServer.listen(process.env.PORT || 3000, () => {
        console.log('  App is running at http://localhost:%d in %s mode', process.env.PORT || 3000, app.get('env'));
        console.log('  Press CTRL-C to stop\n');
    });

    // Allow maximum 20 mongo connection listener
    process.setMaxListeners(20);

    // Init agenda
    global.agendaInstance = new Agenda();
})();

作为MEAN技术栈新手,我是否遗漏了某些配置或步骤导致应用崩溃?恳请提供排查思路。

排查思路

1. 查看具体错误信息

Nodemon的提示只说明崩溃,没有细节。直接用Node运行项目:node server.js,控制台会输出完整错误栈,这是定位问题的核心步骤。

2. 检查依赖与版本兼容性

  • 执行npm install确认所有依赖已安装,无安装报错;
  • 部分包在Node v16下可能存在兼容问题:比如body-parser已被Express 4.16+内置,单独安装的版本可能和当前Express冲突;swagger-jsdoc、agenda等包需确认是否支持Node v16,可查看package.json中的版本号,尝试升级或降级到兼容版本。

3. 验证.env文件配置

代码依赖dotenv加载环境变量,若文件缺失或关键配置错误会直接崩溃:

  • 确认项目根目录存在.env文件,包含必要配置:
    PORT=3000
    NODE_ENV=development
    agenda.dbUri=mongodb://localhost:27017/your-db-name
    
  • 检查initGlobalVariables()是否正确初始化global.configs,确保global.configs.agenda.dbUri能正常读取。

4. 确认文件/目录存在性与权限

代码中用到的路径需逐一验证:

  • src/views:确认目录存在,且有.hbs模板文件;
  • public、public/images:确认目录存在;
  • logs目录:代码会自动创建,但需确保当前用户有读写权限,可手动创建目录测试。

5. 给异步代码添加错误捕获

当前自执行async函数无错误捕获,一旦initGlobalVariables()、路由导入或Agenda初始化出错,会直接崩溃且无日志:

  • 添加try-catch包裹核心代码:
    (async () => {
        try {
            // 原代码内容
        } catch (err) {
            console.error('应用初始化失败:', err);
            process.exit(1);
        }
    })();
    

6. 检查MongoDB连接状态

Agenda依赖MongoDB,若服务未启动或连接字符串错误会导致崩溃:

  • 确认MongoDB服务正在运行;
  • 用MongoDB Compass或命令行测试agenda.dbUri的连接是否正常。

7. 验证路由模块正确性

import('./routes')导入的模块需正确导出接收app参数的函数:

  • 检查routes/index.js(或routes.ts)的导出逻辑;
  • 单独运行路由模块代码,确认无语法或逻辑错误。

8. 清理缓存与重启环境

  • 清理Node缓存:npm cache clean --force,重新安装依赖;
  • 重启终端和IDE,避免残留进程占用资源;
  • 检查端口占用:用netstat -ano | findstr :3000(Windows)确认端口未被其他程序占用。

内容的提问来源于stack exchange,提问作者lands homes

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 20:39:56