Node.js 入门篇:从零搭建 RESTful API
背景
Node.js 是最流行的后端 JavaScript 运行时。本教程带你从零搭建一个完整的 RESTful API 服务,涵盖路由、中间件、数据库操作。
核心概念
1. 模块系统
Node.js 使用 CommonJS(require / module.exports),也支持 ES Modules(import / export,需 .mjs 或 package.json 中 "type":"module")。
const express = require('express');
module.exports = myFunction;
import express from 'express';
export default myFunction;
2. 事件循环(Event Loop)
Node.js 单线程通过事件循环处理异步操作。理解宏任务(setTimeout)和微任务(Promise)的执行顺序是调试的关键。
3. 中间件模式
Express/Koa 的核心思想:请求依次经过多个中间件,每个中间件可:
- 执行代码
- 修改 req/res 对象
- 结束请求(
res.send())
- 调用下一个中间件(
next())
分步操作
第一步:初始化项目
mkdir my-api &
npm init -y
npm install express cors morgan
npm install -D nodemon
第二步:基础服务器
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;
app.use(express.json());
app.listen(PORT, () => console.log(`Server on port ${PORT}`));
第三步:路由与控制器分离
src/
routes/
users.js
controllers/
usersController.js
models/
user.js
app.js
第四步:集成数据库(SQLite)
npm install better-sqlite3
const Database = require('better-sqlite3');
const db = new Database('app.db');
db.exec(`CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL
)`);
第五步:错误处理中间件
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Internal Server Error' });
});
项目最终结构
my-api/
├── package.json
├── src/
│ ├── app.js # Express 应用入口
│ ├── db.js # 数据库连接
│ ├── routes/
│ │ └── users.js # 用户路由
│ ├── controllers/
│ │ └── usersController.js
│ └── middleware/
│ └── errorHandler.js
└── app.db # SQLite 数据文件
思考题
- 如果 10,000 个请求同时到达,Node.js 单线程如何处理?
- 中间件
app.use() 的顺序为什么重要?如果错误处理中间件放在路由之前会怎样?
- CommonJS 和 ES Modules 在循环依赖处理上有何不同?
Node.js 进阶篇:异步编程与性能优化
背景
Node.js 的异步模型是其性能核心。理解 Promise、async/await、Worker Threads 以及 Stream,是编写高性能 Node.js 应用的关键。
核心概念
1. Promise 链与 async/await
fs.readFile('a.txt', (err, dataA) => {
fs.readFile('b.txt', (err, dataB) => {
fs.readFile('c.txt', (err, dataC) => {
console.log(dataA + dataB + dataC);
});
});
});
Promise.all([
fs.promises.readFile('a.txt'),
fs.promises.readFile('b.txt'),
fs.promises.readFile('c.txt'),
]).then(buffers => console.log(buffers.join('')));
const [a, b, c] = await Promise.all([
fs.promises.readFile('a.txt'),
fs.promises.readFile('b.txt'),
fs.promises.readFile('c.txt'),
]);
2. Stream — 流式处理大数据
const fs = require('fs');
const zlib = require('zlib');
fs.createReadStream('large.log')
.pipe(zlib.createGzip())
.pipe(fs.createWriteStream('large.log.gz'));
3. Worker Threads — 突破单线程限制
const { Worker } = require('worker_threads');
function runCPUIntensiveTask(data) {
return new Promise((resolve, reject) => {
const worker = new Worker('./worker.js', { workerData: data });
worker.on('message', resolve);
worker.on('error', reject);
});
}
const { parentPort, workerData } = require('worker_threads');
let result = fibonacci(workerData.n);
parentPort.postMessage(result);
4. Cluster — 利用多核 CPU
const cluster = require('cluster');
const os = require('os');
if (cluster.isPrimary) {
os.cpus().forEach(() => cluster.fork());
cluster.on('exit', (worker) => cluster.fork()); // 自动重启
} else {
require('./app'); // 启动 Express 服务
}
性能优化检查清单
| 优化项 |
方法 |
| 数据库查询 |
使用连接池、添加索引、避免 N+1 查询 |
| 缓存 |
Redis 缓存热点数据、设置合理 TTL |
| 压缩 |
启用 gzip/br 压缩响应 |
| 静态资源 |
使用 CDN 或 Nginx 反代 |
| 内存泄漏 |
使用 --inspect + Chrome DevTools 排查 |
| 事件循环阻塞 |
避免同步 I/O、大循环拆分为 setImmediate |
分步操作:压测与排查
npm i -g autocannon
autocannon -c 100 -d 10 http:
npm i -g clinic
clinic doctor -- node app.js
app.use((req, res, next) =>
const start = Date.now()
res.on('finish', () =>
console.log(`${req.method} ${req.url} ${Date.now() - start}ms`)
})
next()
})
思考题
Promise.all 和 Promise.allSettled 有什么区别?什么场景用哪个?
- 为什么 Worker Threads 不适合处理大量 I/O 操作?
- Stream 的背压(backpressure)机制是如何工作的?
Node.js 毕设实战教程
前言
Node.js 是大学毕设的热门选择,因为它一门语言打穿前后端。本教程将带你从零构建一个完整的毕设后端。
第一章:理解事件循环
Node.js 的核心是 事件循环(Event Loop)。与传统的多线程模型不同,Node.js 用单线程处理所有请求:
请求1 ──→ [Event Loop] ──→ 非阻塞I/O ──→ 回调1
请求2 ──→ ──→ 非阻塞I/O ──→ 回调2
请求3 ──→ ──→ CPU任务 ──→ 回调3
关键理解:CPU 密集任务会阻塞事件循环!一个大循环会让所有请求卡住。所以 Node.js 擅长 I/O 密集型(API 网关、实时聊天),不擅长视频编码、图像渲染。
app.get('/slow', (req, res) => {
for (let i = 0; i < 1e10; i++) {} // 所有请求都卡住
res.send('done');
});
app.get('/fast', async (req, res) => {
const data = await fetchDataFromDB(); // 不阻塞
res.json(data);
});
第二章:项目结构最佳实践
毕设项目推荐的分层结构:
my-project/
├── src/
│ ├── controllers/ # 处理 HTTP 请求
│ ├── services/ # 业务逻辑
│ ├── models/ # 数据模型(Mongoose/Prisma)
│ ├── middlewares/ # 认证、日志、错误处理
│ ├── routes/ # 路由定义
│ ├── utils/ # 工具函数
│ └── app.js # Express 实例
├── tests/
├── .env # 环境变量
└── package.json
为什么这么分?
- Controller 只负责取参数、调 Service、返回响应
- Service 包含所有业务逻辑,可复用、可测试
- Model 定义数据结构,与数据库交互
第三章:认证系统(JWT)
毕设中几乎每个项目都需要登录。JWT 是最简单的方案:
const jwt = require('jsonwebtoken');
app.post('/login', (req, res) => {
const { username, password } = req.body;
const token = jwt.sign(
{ userId: user.id, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '7d' }
);
res.json({ token });
});
const authMiddleware = (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) return res.status(401).json({ error: '请先登录' });
try {
req.user = jwt.verify(token, process.env.JWT_SECRET);
next();
} catch {
res.status(401).json({ error: 'Token 已过期' });
}
};
app.get('/profile', authMiddleware, (req, res) => {
res.json({ userId: req.user.userId });
});
第四章:数据库选择指南
| 数据库 |
适用场景 |
Node.js ORM |
| MongoDB |
灵活文档、快速原型 |
Mongoose |
| PostgreSQL |
关系型、复杂查询 |
Prisma / TypeORM |
| SQLite |
毕设演示、单文件 |
better-sqlite3 |
| MySQL |
传统 Web 应用 |
Prisma / Sequelize |
推荐:毕设用 SQLite + Prisma,零配置,答辩时直接复制 .db 文件即可演示。
第五章:部署上线
npm install -g pm2
pm2 start src/app.js --name my-api
pm2 save
pm2 startup
思考题
- 如果有个请求需要处理 10 秒,如何避免阻塞其他请求?
- JWT Token 存在前端哪里最安全?(提示:Cookie vs localStorage)
- 10 万用户同时在线,Node.js 单进程够用吗?(提示:PM2 cluster mode)