主题
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 dotenvjavascript
// 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,改用结构化日志库(如 winston、pino)。
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 vitestjavascript
// 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 --inittsconfig.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 tsx → tsx src/app.ts)。
代码规范
bash
npm install -D eslint prettiereslint:静态检查,捕获潜在 bug 与不良写法。prettier:统一代码格式,消除风格争论。- 配合 Git Hooks(
husky+lint-staged)在提交前自动检查。
部署清单
- [ ]
NODE_ENV=production已设置 - [ ] 密钥来自环境变量,未硬编码
- [ ] 进程管理器(PM2 / Docker)保障自愈
- [ ] 健康检查接口(如
/healthz)就绪 - [ ] 日志输出到 stdout(容器环境友好)
- [ ] 监听
SIGTERM实现优雅退出
作者:yanshaodong