Node.js新手入门:我在成都写代码时踩过的坑

Shell脚本侠
2025-12-25 02:26
阅读 1425

上周五晚上九点半,我戴着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}`);
});

注意几个关键点:

  1. express.json() 是必须的,否则 req.body 拿不到POST数据
  2. 所有API路径加 /api 前缀,方便后续做Nginx反向代理
  3. 错误处理中间件放在最后,这是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

后续想深入?可以尝试:

  1. 加JWT鉴权(防止随便删别人任务)
  2. 用Prisma连接PostgreSQL
  3. 写单元测试(Jest + Supertest)
  4. 容器化(Dockerfile)

写在最后:前端学Node.js,值不值?

值!太值了!

自从会了Node.js,我不再是“只会切图的前端”。上周我还用它写了个脚本,自动把设计稿的Sketch文件转成TypeScript类型定义——设计师再也不用追着我要字段名了。

在成都这座慢城市,技术人的成长不能慢。Rust我还在啃,但Node.js已经成了我的瑞士军刀。它让我明白:真正的全栈,不是什么都会,而是在需要时能快速解决问题

所以,别再说“我是前端,这不该我干”。技术没有边界,只有解决问题的能力。

对了,如果你也在用Cursor写Node.js,欢迎交流快捷键技巧——它那个 /edit 功能配合正则,重构代码快得飞起。

Happy coding!🎧

评论 0

最热最新
暂无评论
Shell脚本侠Lv.1
0
影响力
0
文章
0
粉丝