[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"site-settings":3,"site-stats":4,"post-10":5},true,null,[6,26],{"locked":7,"post":8},false,{"id":9,"authorId":10,"categoryId":10,"tagId":11,"title":12,"tip":13,"coverUrl":14,"content":15,"videoUrl":4,"views":16,"likes":9,"isPublished":3,"isRecommended":7,"isPinned":7,"allowComment":3,"createdAt":17,"updatedAt":18,"deletedAt":4,"category":19,"tag":21,"author":23,"liked":7},10,1,2,"Node.js + Express 搭建 RESTful API 实战","从零搭一个结构清晰的 Express 服务，路由、校验、错误处理一次讲清","https:\u002F\u002Fimg.sanshijiu.cn\u002Fpost\u002Fcover_02.jpg","## 项目结构\n\n```text\nsrc\u002F\n├── routes\u002F       # 路由定义\n├── controllers\u002F  # 业务入口\n├── services\u002F     # 业务逻辑\n├── middlewares\u002F  # 中间件\n└── app.js\n```\n\n## 统一响应格式\n\n```js\nres.ok = (data) => res.json({ code: 0, data });\nres.fail = (msg, code = 1) => res.json({ code, msg });\n```\n\n## 参数校验\n\n用 zod 在入口处做校验，脏数据进不了业务层：\n\n```js\nconst schema = z.object({ title: z.string().min(1).max(128) });\nconst parsed = schema.safeParse(req.body);\nif (!parsed.success) return res.fail('参数错误');\n```\n\n## 错误处理\n\n所有同步与异步异常统一交给最后的错误中间件，避免进程崩溃。\n\n## 写在最后\n\nExpress 上手快，但\"快\"不等于\"乱\"。把分层和约定定好，小项目也能长成大项目。",295,"2026-07-06T18:46:40.000Z","2026-10-01T01:46:41.275Z",{"id":10,"name":20,"color":11},"后端",{"id":11,"name":22},"Node.js",{"id":10,"nickname":24,"avatarUrl":25},"叁拾玖","https:\u002F\u002Fimg.sanshijiu.cn\u002Favatar\u002F1\u002F1787397803188bebb136a.jpg",{"prev":27,"next":30},{"id":28,"title":29},9,"Spring Boot 集成 JWT 认证全流程",{"id":31,"title":32},11,"MySQL 索引与慢查询优化笔记"]