Node.js新手入门:我在成都写代码时踩过的坑
上周五晚上九点半,我戴着AirPods听着Lo-fi Beats,一边敲着Rust的match表达式,一边突然被拉进一个紧急需求群。产品经理发来一张模糊的原型图:“后端同学下周才能介入,能不能先用Node.js搭个Mock API?就三天。”
我当时差点把键盘扔了——谁说前端不用懂后端?在成都这种生活节奏慢的城市,我也想过安逸地只写Vue组件,但现实是:全栈能力早已不是“加分项”,而是“保命符”。
说来惭愧,虽然我试过Copilot、CodeWhisperer、Tabnine,最后还是回归到Cursor——它对上下文的理解和重构建议真的香。尤其是在调试异步逻辑时,光靠console.log根本不够看。
今天这篇教程,就是给像我一样“被迫全栈”的前端同学准备的。咱们不讲理论大饼,直接上手一个真实小项目:用Node.js搭建一个任务管理API。代码已开源到GitHub,文末会放链接。
为什么前端要学Node.js?
别急着反驳:“我是前端,为什么要碰服务器?” 问得好。但你有没有遇到过这些场景:
- 本地开发时,mock数据写在JSON文件里,改个字段要手动重启服务
- 想做个自动化脚本批量处理图片,结果发现浏览器环境跑不了fs模块
- 公司内部工具没人维护,连个简单的登录页都要等Java组排期
去年双11前夜,我们团队就因为Mock服务没权限控制,测试同学误删了线上缓存配置(别问,问就是血泪史)。从那以后,我下定决心:前端必须掌握基础的服务端能力。
Node.js正好是最佳入口——语法还是JavaScript,生态又极其丰富。哪怕你只用它写构建脚本、CLI工具,效率也能翻倍。
项目目标:做一个极简的任务管理API
我们要实现的功能非常克制:
GET /tasks:获取所有任务POST /tasks:创建新任务DELETE /tasks/:id:删除指定任务
技术栈选择:
- 运行时:Node.js 18+
- Web框架:Express(轻量、社区成熟)
- 数据存储:内存数组(为简化,暂不用数据库)
- 开发工具:nodemon(自动重启)、ESLint
为什么不用Koa或Fastify?不是它们不好,而是Express的中间件生态最友好,文档也最全。新手别一上来就追求“最新最潮”,能快速交付才是王道。
初始化项目 & 目录结构
先建个文件夹:
mkdir task-api && cd task-api
npm init -y
安装核心依赖:
npm install express
npm install --save-dev nodemon
然后调整 package.json 的scripts:
{
"scripts": {
"dev": "nodemon server.js",
"start": "node server.js"
}
}
目录结构长这样:
task-api/
├── server.js # 入口文件
├── routes/
│ └── tasks.js # 路由逻辑
├── utils/
│ └── logger.js # 简易日志
└── package.json
写第一个Express服务器
server.js 内容如下:
// server.js
const express = require('express');
const tasksRouter = require('./routes/tasks');
const app = express();
const PORT = process.env.PORT || 3000;
// 中间件:解析JSON请求体
app.use(express.json());
// 挂载路由
app.use('/api', tasksRouter);
// 全局错误处理(简化版)
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Something broke!' });
});
app.listen(PORT, () => {
console.log(`🚀 Server running on http://localhost:${PORT}`);
});
注意几个关键点:
express.json()是必须的,否则req.body拿不到POST数据- 所有API路径加
/api前缀,方便后续做Nginx反向代理 - 错误处理中间件放在最后,这是Express的约定
实现任务路由
现在重点来了:routes/tasks.js
// routes/tasks.js
const express = require('express');
const router = express.Router();
// 用内存存储任务(生产环境请用数据库!)
let tasks = [
{ id: 1, title: '学习Node.js', completed: false },
{ id: 2, title: '写一篇教程', completed: true }
];
let nextId = 3;
// GET /api/tasks
router.get('/tasks', (req, res) => {
res.json(tasks);
});
// POST /api/tasks
router.post('/tasks', (req, res) => {
const { title } = req.body;
// 简单校验
if (!title || typeof title !== 'string') {
return res.status(400).json({ error: 'Title is required and must be a string' });
}
const newTask = {
id: nextId++,
title,
completed: false
};
tasks.push(newTask);
res.status(201).json(newTask);
});
// DELETE /api/tasks/:id
router.delete('/tasks/:id', (req, res) => {
const id = parseInt(req.params.id, 10);
const index = tasks.findIndex(task => task.id === id);
if (index === -1) {
return res.status(404).json({ error: 'Task not found' });
}
tasks.splice(index, 1);
res.status(204).send(); // 204 No Content
});
module.exports = router;
这里有几个新手容易踩的坑:
- ID生成:别用数组长度当ID!删除元素后长度会变,导致ID重复。我们用独立的
nextId变量。 - 状态码:创建资源用
201 Created,删除成功用204 No Content(表示响应体为空),这是RESTful规范。 - 参数解析:
req.params.id是字符串,必须用parseInt转成数字,否则===比较会失败。
测试API:别只靠Postman
我知道很多人会立刻打开Postman,但命令行其实更快:
# 获取任务列表
curl http://localhost:3000/api/tasks
# 创建新任务
curl -X POST http://localhost:3000/api/tasks \
-H "Content-Type: application/json" \
-d '{"title":"修复bug"}'
# 删除任务(假设ID=3)
curl -X DELETE http://localhost:3000/api/tasks/3
如果返回 400 Bad Request,八成是JSON格式错了——记得检查引号是不是英文的!
加点“前端味”:CORS与静态文件
现在有个问题:如果你的前端React/Vue应用跑在 http://localhost:5173,直接调这个API会跨域失败。
解决方法很简单,装个cors包:
npm install cors
然后在 server.js 里加上:
const cors = require('cors');
app.use(cors()); // 允许所有来源(开发环境OK,生产环境要限制!)
另外,如果你想顺便托管前端页面(比如一个简易的管理界面),Express也能干:
// 在server.js的路由之后加
app.use(express.static('public'));
然后把 index.html 放到 public/ 目录下,访问根路径就能看到页面。不过这只是演示,前后端分离项目通常用Nginx托管静态资源。
日志、错误与调试技巧
上面代码里我提到了logger,其实可以更智能点。比如记录每个请求的耗时:
// utils/logger.js
function logger(req, res, next) {
const start = Date.now();
const url = req.url;
res.on('finish', () => {
const duration = Date.now() - start;
console.log(`[${new Date().toISOString()}] ${req.method} ${url} - ${res.statusCode} (${duration}ms)`);
});
next();
}
module.exports = logger;
然后在 server.js 里:
const logger = require('./utils/logger');
app.use(logger);
输出效果:
[2024-06-15T14:30:00.000Z] GET /api/tasks - 200 (12ms)
[2024-06-15T14:31:05.000Z] POST /api/tasks - 201 (8ms)
这种日志在排查性能瓶颈时特别有用。有一次我们发现某个接口平均耗时2秒,查日志才发现是数据库连接池满了——没有日志的系统就像盲人开车。
部署到线上?先搞定这些
本地跑得欢,上线就翻车。部署前务必检查:
| 项目 | 开发环境 | 生产环境 |
|---|---|---|
| 端口 | 固定3000 | 读取 process.env.PORT |
| 日志 | console.log | 接入Winston或云服务商日志 |
| 错误信息 | 返回详细错误 | 返回通用提示,避免泄露敏感信息 |
| 静态资源 | Express托管 | CDN + Nginx |
另外,千万别把内存当数据库!我们的 tasks 数组在进程重启后就清空了。真要持久化,至少用SQLite,或者上MongoDB(对JS开发者友好)。
GitHub开源 & 后续优化
我把完整代码放到了GitHub:https://github.com/yourname/nodejs-task-api-tutorial (替换为你的真实仓库)
你可以直接clone下来跑:
git clone https://github.com/yourname/nodejs-task-api-tutorial
cd nodejs-task-api-tutorial
npm install
npm run dev
后续想深入?可以尝试:
- 加JWT鉴权(防止随便删别人任务)
- 用Prisma连接PostgreSQL
- 写单元测试(Jest + Supertest)
- 容器化(Dockerfile)
写在最后:前端学Node.js,值不值?
值!太值了!
自从会了Node.js,我不再是“只会切图的前端”。上周我还用它写了个脚本,自动把设计稿的Sketch文件转成TypeScript类型定义——设计师再也不用追着我要字段名了。
在成都这座慢城市,技术人的成长不能慢。Rust我还在啃,但Node.js已经成了我的瑞士军刀。它让我明白:真正的全栈,不是什么都会,而是在需要时能快速解决问题。
所以,别再说“我是前端,这不该我干”。技术没有边界,只有解决问题的能力。
对了,如果你也在用Cursor写Node.js,欢迎交流快捷键技巧——它那个 /edit 功能配合正则,重构代码快得飞起。
Happy coding!🎧

评论 0