Skip to content

Node.js 工程化

从"能跑的脚本"到"可维护的服务",需要规范的工程实践:项目结构、配置管理、日志、错误处理、测试与 TypeScript 集成。

作者:yanshaodong

项目结构

推荐按职责分层,而非按技术分层:

my-service/
├── src/
│   ├── config/          # 配置加载
│   ├── controllers/     # 请求处理
│   ├── services/        # 业务逻辑
│   ├── models/          # 数据模型
│   ├── middleware/       # 中间件
│   ├── utils/           # 工具函数
│   └── app.js           # 入口
├── tests/               # 测试
├── .env                 # 本地环境变量(不入库)
├── .env.example         # 环境变量模板(入库)
├── package.json
└── README.md

配置与环境变量

使用 dotenv 管理配置,区分环境(dev / test / prod)。

bash
npm install dotenv
javascript
// config/index.js
require('dotenv').config()

module.exports = {
  port: process.env.PORT || 3000,
  env: process.env.NODE_ENV || 'development',
  dbUrl: process.env.DATABASE_URL,
  jwtSecret: process.env.JWT_SECRET
}

.env.example(提交到仓库):

bash
PORT=3000
NODE_ENV=development
DATABASE_URL=postgres://localhost:5432/app
JWT_SECRET=change_me

严禁将含密钥的 .env 提交到 Git,应在 .gitignore 中忽略。

日志

避免直接使用 console.log,改用结构化日志库(如 winstonpino)。

javascript
const winston = require('winston')

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.json()
  ),
  transports: [new winston.transports.Console()]
})

logger.info('服务启动', { port: 3000 })
logger.error('数据库连接失败', { err: err.message })

生产环境建议输出 JSON 格式日志,便于 ELK / Loki 收集。

统一错误处理

javascript
// 异步错误捕获中间件(Express 示例)
function errorHandler(err, req, res, next) {
  logger.error('请求出错', { err: err.message, stack: err.stack })
  const status = err.status || 500
  res.status(status).json({
    code: status,
    message: status === 500 ? '服务器内部错误' : err.message
  })
}

// 包装 async 路由,避免遗漏 catch
function asyncHandler(fn) {
  return (req, res, next) => fn(req, res, next).catch(next)
}

单元测试

推荐使用 Vitest(与 Vite 同源、速度快)或 Jest

bash
npm install -D vitest
javascript
// math.test.js
import { describe, it, expect } from 'vitest'
import { add } from '../src/math'

describe('add', () => {
  it('1 + 2 = 3', () => {
    expect(add(1, 2)).toBe(3)
  })
})

package.json 脚本:

json
{
  "scripts": {
    "test": "vitest run",
    "test:watch": "vitest",
    "start": "node src/app.js",
    "dev": "node --watch src/app.js"
  }
}

Node 18+ 支持 node --watch 实现文件变更自动重启,开发期无需 nodemon。

TypeScript 集成

bash
npm install -D typescript @types/node
npx tsc --init

tsconfig.json 关键配置:

json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "outDir": "dist",
    "strict": true,
    "esModuleInterop": true
  }
}

执行:tsc 编译后 node dist/app.js;或用 tsx 直接运行 TS(npm i -D tsxtsx src/app.ts)。

代码规范

bash
npm install -D eslint prettier
  • eslint:静态检查,捕获潜在 bug 与不良写法。
  • prettier:统一代码格式,消除风格争论。
  • 配合 Git Hooks(husky + lint-staged)在提交前自动检查。

部署清单

  • [ ] NODE_ENV=production 已设置
  • [ ] 密钥来自环境变量,未硬编码
  • [ ] 进程管理器(PM2 / Docker)保障自愈
  • [ ] 健康检查接口(如 /healthz)就绪
  • [ ] 日志输出到 stdout(容器环境友好)
  • [ ] 监听 SIGTERM 实现优雅退出

作者:yanshaodong