Node.js 以事件驱动、非阻塞 I/O 和单线程事件循环著称,适合构建高并发的 I/O 密集型服务。Express 则以其极简的中间件模型,成为 Node.js 生态中最广泛使用的 Web 框架之一。然而,“能跑起来”与“生产级可靠”之间,隔着工程约束、分层设计、安全、可观测性和性能治理。本文从专业角度,梳理一套可落地的 Node.js + Express 架构方法。
Express 的本质不是“框架魔法”,而是一条中间件管道。每个中间件都是 (req, res, next) 函数,按注册顺序执行;路由是中间件的一种;错误处理中间件则通过四个参数 (err, req, res, next) 识别。
const express = require('express');
const app = express();
app.use(express.json({ limit: '1mb' }));
app.get('/health', (req, res) => {
res.json({ ok: true });
});
app.use((err, req, res, next) => {
const status = err.statusCode || 500;
res.status(status).json({
error: {
code: err.code || 'INTERNAL_ERROR',
message: status >= 500 ? '服务器内部错误' : err.message,
},
});
});专业项目应将 app 与 server 分离,便于测试和部署:
// app.js
const app = express();
// ...中间件与路由
module.exports = app;
// server.js
const app = require('./app');
const server = app.listen(process.env.PORT || 3000);Express 很自由,但自由意味着容易失控。推荐按职责分层:
src/
routes/ # 路由定义
controllers/ # 请求编排、参数提取
services/ # 业务逻辑
repositories/ # 数据访问
middlewares/ # 认证、校验、日志
schemas/ # 输入校验
config/ # 配置
app.js
server.js路由层只负责映射:
router.post('/users', validate(createUserSchema), userController.create);Controller 调用 Service,Service 调用 Repository。这样业务逻辑可单元测试,数据库可替换,职责边界清晰。
Express 5 已支持 async handler 返回 rejected Promise 时自动转发给错误中间件。若使用 Express 4,则需要包装器:
const asyncHandler = (fn) => (req, res, next) =>
Promise.resolve(fn(req, res, next)).catch(next);建议定义统一的可操作错误类:
class AppError extends Error {
constructor(message, statusCode, code) {
super(message);
this.statusCode = statusCode;
this.code = code;
this.isOperational = true;
}
}错误中间件应区分“可预期错误”和“程序错误”。前者返回明确状态码,后者记录日志并隐藏堆栈。
在系统边界验证输入,内部信任类型。可使用 Zod、Joi 或 express-validator。
const { z } = require('zod');
const createUserSchema = z.object({
email: z.string().email(),
name: z.string().min(1).max(50),
password: z.string().min(8),
});
const validate = (schema) => (req, res, next) => {
const result = schema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({
error: {
code: 'VALIDATION_ERROR',
details: result.error.flatten(),
},
});
}
req.body = result.data;
next();
};避免直接信任 req.query、req.params,防止原型污染和类型混淆。
生产级 Express 应用至少应包含:
helmet 设置安全响应头;cors 使用白名单,避免 * 与 credentials 共用;express-rate-limit 做限流;argon2 或 bcrypt;httpOnly、secure、sameSite;app.set('trust proxy', 1) 配合反向代理;npm audit、Snyk、Dependabot;.env。app.use(helmet());
app.use(cors({ origin: ['https://example.com'], credentials: true }));
app.use('/api', rateLimit({ windowMs: 60_000, max: 100 }));Node.js 适合 I/O 密集,但单线程不适合 CPU 密集。CPU 重任务应使用 worker_threads 或独立服务。
关键实践:
pino 输出结构化日志,并注入 request id;/health/live 与 /health/ready;compression;server.requestTimeout、headersTimeout;process.on('SIGTERM', () => {
server.close(async () => {
await db.close();
process.exit(0);
});
});多核利用可通过容器多副本、PM2 cluster 或 Node cluster 实现。可观测性建议接入 OpenTelemetry、Prometheus 和 Grafana。
supertest 直接请求 app;const request = require('supertest');
const app = require('../src/app');
test('GET /health', async () => {
const res = await request(app).get('/health');
expect(res.status).toBe(200);
});fs.readFileSync 等同步阻塞 API;console.log 而不处理;Node.js + Express 的优势是轻量、灵活、生态成熟。但生产级质量并非来自框架本身,而来自工程约束:中间件管道、分层架构、输入验证、统一错误处理、安全基线、可观测性、性能治理和部署规范。把 Express 当作 HTTP 编排层,把业务逻辑放入可测试的 Service 层,才能从快速原型走向可持续的生产系统。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。