Node.js 凭借异步 I/O 与单线程事件循环,特别适合构建高并发的 API 服务。本文用 Express 从零搭建一个生产级 RESTful API,覆盖路由设计、中间件、数据库连接、统一错误处理,并给出可复用的分层架构骨架。
一、项目初始化与目录结构
一个健康的 Node.js 项目应当分层清晰。推荐如下目录结构:
api-server/
├── src/
│ ├── config/ # 配置(环境变量、数据库)
│ ├── routes/ # 路由定义
│ ├── controllers/ # 控制器:处理请求响应
│ ├── services/ # 业务逻辑
│ ├── models/ # 数据模型
│ ├── middlewares/ # 自定义中间件
│ └── app.js # Express 应用入口
├── .env
└── package.json
# 初始化
mkdir api-server && cd api-server
npm init -y
npm i express mongoose dotenv cors helmet morgan
npm i -D nodemon
二、Express 应用与中间件
app.js 是应用入口,负责挂载全局中间件与路由。中间件的挂载顺序很重要:日志、安全头应最先,业务路由居中,错误处理最后。
// src/app.js
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
require('dotenv').config();
const postRoutes = require('./routes/post.routes');
const errorHandler = require('./middlewares/error.middleware');
const app = express();
// 全局中间件
app.use(helmet()); // 安全 HTTP 头
app.use(cors()); // 跨域
app.use(express.json()); // 解析 JSON body
app.use(morgan('dev')); // 请求日志
// 健康检查
app.get('/health', (req, res) => res.json({ status: 'ok' }));
// 业务路由
app.use('/api/posts', postRoutes);
// 404 兜底
app.use((req, res) => res.status(404).json({ error: 'Not Found' }));
// 错误处理(必须最后挂载)
app.use(errorHandler);
module.exports = app;
三、路由与控制器分层
路由只负责"声明 URL → 控制器方法",业务逻辑下沉到 service。这样路由层薄、易读,也便于测试。
// src/routes/post.routes.js
const router = require('express').Router();
const ctrl = require('../controllers/post.controller');
const auth = require('../middlewares/auth.middleware');
router.get('/', ctrl.list);
router.get('/:id', ctrl.getById);
router.post('/', auth, ctrl.create);
router.put('/:id', auth, ctrl.update);
router.delete('/:id', auth, ctrl.remove);
module.exports = router;
// src/controllers/post.controller.js
const postService = require('../services/post.service');
exports.list = async (req, res, next) => {
try {
const { page = 1, size = 20 } = req.query;
const data = await postService.list({ page, size });
res.json(data);
} catch (err) { next(err); } // 交给错误中间件
};
exports.create = async (req, res, next) => {
try {
const post = await postService.create(req.body);
res.status(201).json(post);
} catch (err) { next(err); }
};
RESTful 设计要点:用 HTTP 方法表达动作(GET 查/POST 增/PUT 改/DELETE 删),用状态码表达结果(201 创建、204 无内容、400 参数错、401 未认证、404 不存在、500 服务器错)。
四、数据库连接与模型
以 MongoDB + Mongoose 为例。连接抽到 config,模型定义字段结构与校验。
// src/config/db.js
const mongoose = require('mongoose');
module.exports = async function connectDB() {
const uri = process.env.MONGO_URI;
await mongoose.connect(uri);
console.log('MongoDB 已连接');
};
// src/models/post.model.js
const { Schema, model } = require('mongoose');
const postSchema = new Schema({
title: { type: String, required: true, trim: true, maxlength: 120 },
content: { type: String, required: true },
author: { type: Schema.Types.ObjectId, ref: 'User' },
tags: [String],
}, { timestamps: true }); // 自动 createdAt/updatedAt
module.exports = model('Post', postSchema);
五、统一错误处理
集中错误处理是生产级 API 的标配。自定义 ApiError 携带状态码,全局中间件统一格式化响应。
// src/middlewares/error.middleware.js
class ApiError extends Error {
constructor(status, message) {
super(message);
this.status = status;
}
}
module.exports = function errorHandler(err, req, res, next) {
const status = err.status || 500;
const message = err.message || '服务器内部错误';
// 开发环境返回堆栈,生产环境隐藏
const body = { error: message };
if (process.env.NODE_ENV !== 'production') {
body.stack = err.stack;
}
// 记录 5xx 错误日志
if (status >= 500) console.error(err);
res.status(status).json(body);
};
// service 中抛出业务错误
const post = await Post.findById(id);
if (!post) throw new ApiError(404, '文章不存在');
六、启动与环境配置
// src/server.js
const app = require('./app');
const connectDB = require('./config/db');
const PORT = process.env.PORT || 3000;
(async () => {
await connectDB();
app.listen(PORT, () => console.log(`API 运行于 :${PORT}`));
})();
// 优雅退出:捕获未处理异常,避免进程崩溃
process.on('unhandledRejection', (err) => {
console.error('未处理的 Promise 拒绝', err);
});
- 配置用环境变量:敏感信息(数据库密码、密钥)放进 .env,绝不硬编码。
- helmet 加固:自动设置安全 HTTP 头,防 XSS、点击劫持。
- 限流:用 express-rate-limit 防暴力请求。
- 参数校验:用 joi 或 zod 在 service 层校验入参。
这套分层骨架可以平稳支撑从小型到中型项目的演进。关键在于"分层 + 统一错误处理":路由薄、service 重、错误集中。当你把这套结构跑通,后续加认证、缓存、队列都是在既定分层上扩展,不会让代码失控。