多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE

技术文章

聚焦一线编程技术原理与实战踩坑,沉淀可复用的工程经验。

Node.js 构建 RESTful API 完整实战

Node.js 构建 RESTful API 完整实战

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 重、错误集中。当你把这套结构跑通,后续加认证、缓存、队列都是在既定分层上扩展,不会让代码失控。

返回列表