【Node.js】实战:从 0 搭建一个任务管理 RESTful API(Node 22 + Express)】
从零搭建一个 Node.js 22 + Express 的任务管理 RESTful API,涵盖环境搭建、项目结构、路由设计、CRUD 实战、错误处理和测试。每个关键步骤都配图 + 可跑代码,适合直接照着敲一遍,然后发到 CSDN 当成自己的 Node.js 实战博文。
目录
一、为什么 2025 年还值得学 Node.js 实战?
二、实战目标:我们要搭什么?
三、整体流程一眼看完
四、环境准备与项目初始化
1. 确认 Node.js 版本(推荐 22 LTS)
2. 初始化项目
3. 安装依赖
五、项目结构:按“功能模块”组织
六、写入口:app.js
七、写路由:routes/tasks.js(核心 CRUD)
八、跑起来 + 用 curl 测试
1. 启动服务器
2. 测试 CRUD
九、加点“工程味”:错误处理 + 输入校验
1. 统一错误响应中间件
2. 更严格的参数校验
十、避坑指南:新手最容易踩的坑
十一、如何把这篇变成你自己的 CSDN 博文?
一、为什么 2025 年还值得学 Node.js 实战?
- Node.js 22 已经进入 LTS,原生支持 TypeScript 执行、内置 WebSocket 客户端、
require(esm)、.env文件等,很多以前必须装包才能干的事,现在运行时自己就能搞定mortexsolutions.com。 - Express 依然是 2025 年最主流的 Node.js Web 框架之一,生态成熟、资料最多jeuxdevelopers.com+1。
- RESTful API 仍然是后端最基础的“通用语言”,学会用 Node.js 搭一套 API,以后不管是接前端、写管理后台还是做微服务,都很有用jianshu.com。
二、实战目标:我们要搭什么?
我们要做一个“任务管理系统(Tasks API)”,功能包括:
- 获取所有任务:
GET /tasks - 获取单个任务:
GET /tasks/:id - 创建新任务:
POST /tasks - 更新任务:
PUT /tasks/:id - 删除任务:
DELETE /tasks/:id
数据先放内存(数组),不连数据库,方便专注理解 Node + Express 本身。
三、整体流程一眼看完
四、环境准备与项目初始化
1. 确认 Node.js 版本(推荐 22 LTS)
node -v # 建议看到 v22.x.x如果版本太旧,去官网下载 22 LTS 安装aliyun.com。
2. 初始化项目
mkdir node-task-api cd node-task-api npm init -y这会生成一个package.json,记录项目信息和依赖aliyun.com。
3. 安装依赖
npm install express npm install --save-dev nodemonexpress:Web 框架,用来定义路由和中间件aliyun.com。nodemon:开发时自动重启服务,改完代码不用自己Ctrl+C再启动aliyun.com。
在package.json的scripts里加一条:
"scripts": { "start": "nodemon app.js" }以后只需要npm start就能跑开发服务器。
五、项目结构:按“功能模块”组织
2025 年比较推荐的 Node.js 项目结构是按功能分层,而不是所有代码塞进一个文件logrocket.com+2。
我们这次用一个简化版结构:
node-task-api/ ├── app.js # 入口:创建 Express 应用,加载路由 ├── package.json ├── package-lock.json └── routes/ └── tasks.js # 任务相关路由💡 秘籍:随着项目变大,你还可以再加
controllers、services、models等目录,把“路由 / 业务逻辑 / 数据访问”分层dev.to。
六、写入口:app.js
// app.js const express = require('express'); const tasksRoutes = require('./routes/tasks'); const app = express(); const PORT = process.env.PORT || 3000; // 1. 中间件:解析 JSON 请求体 app.use(express.json()); // 2. 简单日志中间件(自己写) app.use((req, res, next) => { const start = Date.now(); res.on('finish', () => { const cost = Date.now() - start; console.log(`${req.method}${req.url} - ${res.statusCode} -${cost}ms`); }); next(); }); // 3. 路由:/tasks 下的所有接口 app.use('/tasks', tasksRoutes); // 4. 启动服务器 app.listen(PORT, () => { console.log(`Server is running on port ${PORT}`); });这段代码做了几件事:
- 创建 Express 应用
- 用
express.json()解析请求体,这样req.body才能拿到 JSON 数据aliyun.com - 加了一个简单的日志中间件,方便观察请求耗时
- 把
/tasks路由交给routes/tasks.js处理
七、写路由:routes/tasks.js(核心 CRUD)
// routes/tasks.js const express = require('express'); const router = express.Router(); // 示例数据(内存里的“数据库”) let tasks = [ { id: 1, title: 'Buy groceries', description: 'Milk, Cheese, Pizza, Fruit', done: false, }, { id: 2, title: 'Learn Node.js', description: 'Follow a Node.js tutorial and build a REST API', done: false, }, ]; let nextId = 3; // 获取所有任务 router.get('/', (req, res) => { res.json({ tasks }); }); // 获取单个任务 router.get('/:id', (req, res) => { const taskId = parseInt(req.params.id, 10); const task = tasks.find(t => t.id === taskId); if (!task) { return res.status(404).json({ message: 'Task not found' }); } res.json(task); }); // 创建新任务 router.post('/', (req, res) => { const { title, description } = req.body; if (!title) { return res.status(400).json({ message: 'title is required' }); } const newTask = { id: nextId++, title, description: description || '', done: false, }; tasks.push(newTask); res.status(201).json(newTask); }); // 更新任务 router.put('/:id', (req, res) => { const taskId = parseInt(req.params.id, 10); const task = tasks.find(t => t.id === taskId); if (!task) { return res.status(404).json({ message: 'Task not found' }); } const { title, description, done } = req.body; task.title = title || task.title; task.description = description || task.description; task.done = done !== undefined ? done : task.done; res.json(task); }); // 删除任务 router.delete('/:id', (req, res) => { const taskId = parseInt(req.params.id, 10); const task = tasks.find(t => t.id === taskId); if (!task) { return res.status(404).json({ message: 'Task not found' }); } const index = tasks.indexOf(task); tasks.splice(index, 1); res.json({ result: true }); }); module.exports = router;这个文件的结构参考了阿里云社区一个 2024 年的 Node.js REST 教程,但做了更清晰的错误处理和参数校验aliyun.com。
八、跑起来 + 用 curl 测试
1. 启动服务器
npm start终端会看到:
Server is running on port 30002. 测试 CRUD
获取所有任务:
curl http://localhost:3000/tasks获取单个任务:
curl http://localhost:3000/tasks/1创建新任务:
curl -X POST \ -H "Content-Type: application/json" \ -d '{"title":"Learn Node.js"}' \ http://localhost:3000/tasks更新任务:
curl -X PUT \ -H "Content-Type: application/json" \ -d '{"title":"Master Node.js", "done": true}' \ http://localhost:3000/tasks/3删除任务:
curl -X DELETE http://localhost:3000/tasks/1如果你用 Postman,可以直接把这些 URL 和方法配置进去,更直观csdn.net。
九、加点“工程味”:错误处理 + 输入校验
2025 年的 Node.js API 实战里,输入校验 + 统一错误格式是基本要求dev.to。
1. 统一错误响应中间件
在app.js里加:
// 统一错误响应格式 app.use((err, req, res, next) => { console.error(err.stack); res.status(500).json({ code: 500, message: 'Internal Server Error', error: process.env.NODE_ENV === 'development' ? err.message : undefined, }); });2. 更严格的参数校验
比如创建任务时,要求title必须是非空字符串:
router.post('/', (req, res) => { const { title, description } = req.body; if (!title || typeof title !== 'string' || !title.trim()) { return res.status(400).json({ code: 400, message: 'title must be a non-empty string', }); } // ... 创建任务逻辑 });⚠️ 注意:永远不要信任客户端输入,这是 2025 年 API 安全的第一条原则dev.to。
十、避坑指南:新手最容易踩的坑
端口被占用:
EADDRINUSE- 换一个端口:
PORT=3001 npm start - 或者杀掉占用进程:
lsof -i :3000(macOS/Linux)
- 换一个端口:
请求体解析失败
- 忘记
app.use(express.json())→req.body是undefined - 忘记设置
Content-Type: application/json→ 解析失败
- 忘记
id 类型不一致
- URL 里的
req.params.id是字符串,要parseInt再和数字比较 - 否则
1 === '1'为false,导致找不到任务
- URL 里的
修改数据没重启服务
- 用
nodemon可以自动重启,避免手动重启
- 用
