Odin Book 最终项目实战:用 Node.js、Express 与 PostgreSQL 从零构建社交媒体网站
2026/9/15 11:25:51 网站建设 项目流程

Odin Book 最终项目实战:用 Node.js、Express 与 PostgreSQL 从零构建社交媒体网站

【免费下载链接】curriculumThe open curriculum for learning web development项目地址: https://gitcode.com/GitHub_Trending/cu/curriculum

本篇技术指南围绕仓库中 nodeJS/final_project/project_odin_book.md 这一 NodeJS 课程的收官项目展开。你将面对一份高度开放的高阶需求清单——克隆一个类似 Facebook、X、Myspace 或 Threads 的社交媒体网站,并完整实现用户、资料、帖子、关注、点赞、评论、认证等核心功能。读完本文,你将掌握"把高层面需求转化为可运行网站"的完整方法论:范围规划、数据建模、种子数据生成、Passport.js 认证接入、核心 CRUD 实现与 PaaS 部署,为简历添上一份重量级的全栈作品。

项目背景:为何这是课程的"毕业设计"

课程走到这一步,你已经具备独立构建 Express 应用、并用 PostgreSQL 建模和存储数据的能力。Odin Book 项目要求你把所有这些知识整合起来——它不是一道加试题,而是一次综合演练,难度可控但极具挑战,完成后会是一份极佳的简历作品

从仓库课程结构看,这是 nodeJS 路径的最终项目,紧随其后的是 结课总结,后者明确写道:"你刚刚构建了一个社交媒体网站",并以此宣告你已具备"全栈开发者"的能力。这印证了该项目的定位:它是整个 NodeJS 课程的验收标准。

与课程中此前的项目相比,本项目有几个显著不同的特点:

  • 开放性:需求是"全局性的功能清单",你需要自己决定克隆哪个平台、实现哪些功能、如何实现。
  • 自主研究:你会用到几个此前未深入接触的模块,必须学会阅读官方文档。
  • 数据复杂度:模型数量多、模型间关系复杂,远超你此前做过的任何项目。

在动手前请记住课程反复强调的原则:先把核心功能做完,再碰锦上添花的部分。如果试图一次做完所有事,你会迷失方向并感到挫败——一切都比预期耗时。

技术栈准备:回顾你已掌握的构建模块

本项目会用到你在 NodeJS 路径 中学过的几乎所有关键能力。在开工前,建议快速回顾以下课程的要点,它们将直接支撑本项目:

1. Express 应用骨架与路由

回顾 Express 入门:从npm init -ynpm install express起步,理解请求对象req、响应对象res、中间件链与res.send/res.render/res.redirect的差别。开发时可用node --watch app.js实现文件变更自动重启,端口建议按环境变量读取:const PORT = process.env.PORT || 3000;——这在部署阶段至关重要。

在 路由 中,你会掌握 HTTP 动词(GET/POST/PUT/DELETE)与路径匹配。本项目推荐遵循 MVC 模式组织代码:routes/(路由定义)、controllers/(控制器)、views/(视图模板),具体结构示例见 控制器课程:

express-app/ ├─ errors/ ├─ controllers/ │ ├─ usersController.js │ ├─ postsController.js ├─ routes/ │ ├─ usersRouter.js │ ├─ postsRouter.js ├─ views/ ├─ app.js ├─ db/ 或 prisma/

控制器是"终极中间人":它知道要问模型什么问题、要渲染哪个视图,但把具体实现交给模型和视图。同时要记得把错误处理中间件(四个参数的(err, req, res, next))放在应用末尾,并用自定义错误类(如继承Error并携带statusCode)区分 404 与 500。

2. 数据库:PostgreSQL 建模与查询

你此前已经掌握两种与数据库交互的方式:

  • 原生 SQL +pg:见 使用 PostgreSQL。使用连接池new Pool({...})管理连接,查询务必使用参数化查询$1占位符)以防 SQL 注入。
  • Prisma ORM:见 Prisma ORM 课程。Prisma 将数据库定义(schema)纳入代码库,通过npx prisma migrate管理迁移、npx prisma generate生成类型安全的客户端,并用prisma.message.create({ data: {...} })这类 API 取代手写 SQL。

对 Odin Book 这种多模型、多关系的项目,ORM 的价值会充分体现:关系定义在 schema 中一目了然,迁移有标准化的 changelog,不再需要登录数据库才能理解代码结构。选择pg还是 Prisma 都可以,关键是先想清楚数据架构再动手

3. 认证:Passport.js 全家桶

本项目明确要求实现某种形式的认证,首选 passport.js 中已经完整演练过以下流程:

  • 中间件装配顺序:express-sessionpassport.session()express.urlencoded()
  • passport.use(new LocalStrategy(...))定义用户名密码校验逻辑,返回done(null, user)done(null, false, { message })
  • passport.serializeUser/passport.deserializeUser负责把用户 id 写入会话、再从数据库还原用户并挂到req.user
  • 登录路由只需一行:passport.authenticate("local", { successRedirect, failureRedirect, failureMessage: true })
  • 登出用req.logout((err) => {...})
  • 密码必须用 bcrypt 哈希存储:注册时bcrypt.hash(password, 10)(第二个参数为盐的轮数),校验时bcrypt.compare(password, user.password)

本项目建议优先考虑第三方 OAuth 认证(见下文"认证方案选型"),但如果所选平台关闭了该途径,passport-localpassport-github2都是官方认可的替代方案。

4. 表单处理与安全

帖子、评论、关注请求、注册登录都涉及表单。回顾 表单与数据处理:

  • POST表单需要express.urlencoded({ extended: true })解析到req.body
  • express-validatorbody()链式校验与清洗(.trim().notEmpty().isLength()等),再用validationResult(req)收集错误、matchedData(req)取清洗后的数据;
  • 遵守Post/Redirect/Get(PRG)模式:POST 处理后res.redirect,避免重复提交;
  • 在模板中输出用户内容时使用 EJS 转义语法<%= %>(而非<%- %>),防止 XSS 注入。

开工前规划:范围管理是成功的一半

原文档给出的第一条建议非常具体:把计划写在纸上或白板上。先花几个小时思考,能省下数天编码时间。规划的核心是scope(范围)——你显然无法重建整个网站(它可能是一个完整工程师团队花数年才做出来的产品),所以必须识别:

  • 核心功能:用户注册登录、发帖、点赞、评论、关注、动态流、个人主页、用户列表;
  • 锦上添花:图片上传、实时消息、通知、聊天等。

第二件事是想清楚数据架构:本项目模型众多、关系复杂,远超以往任何项目。建议在动手前完成以下设计:

  • User:用户名、密码哈希、资料(简介、头像 URL 等)、关注关系(自关联多对多);
  • Post:内容、作者(外键)、时间戳;
  • Like/Comment:分别关联用户与帖子;
  • FollowFriendRequest:记录关注/待处理请求的状态。

如果使用 Prisma,这些关系会以@relation(fields: [authorId], references: [id])的形式显式声明在 schema 中(示例见 Prisma ORM 课程 的Message/User模型),让整张数据模型图在代码库里一目了然。

用 Faker 生成种子数据:seeds.js

真实开发中,手工创建用户、帖子来测试界面既痛苦又低效。原文档给出的方案是使用Fakerfaker-js/faker)模块:创建一个名为seeds.js的 JavaScript 文件,导入你的 Prisma 模型(如果使用 Prisma),用 faker 批量生成并保存新用户。

这与你在 使用 PostgreSQL 中学过的"用脚本填充数据库"一脉相承——只是把硬编码的 SQL 换成了 ORM 调用与随机数据。一个可参考的骨架:

// seeds.js —— 使用 Prisma 时 import { PrismaClient } from "@prisma/client"; import { faker } from "@faker-js/faker"; const prisma = new PrismaClient(); async function main() { for (let i = 0; i < 20; i++) { await prisma.user.create({ data: { username: faker.internet.username(), email: faker.internet.email(), password: faker.internet.password(), bio: faker.person.bio(), }, }); } console.log("seeding done"); } main().finally(async () => { await prisma.$disconnect(); });

种子脚本应当"一次运行即可重建演示环境",方便你在开发、演示与部署后快速填充数据。注意密码字段即使是用 faker 生成的随机值,也建议走一遍 bcrypt 哈希流程,保持与真实注册路径一致。

核心需求清单:逐项实现

原文档提供了一份"非常全局"的功能清单。由于项目开放性,并非所有条目都适用于你选定的平台,也可能存在清单未提及但属于该平台核心的功能。以下是 11 项核心需求及其实现要点:

1. 除登录页外,用户必须登录才能看到任何内容

在 Express 中,用中间件保护所有非认证路由。参考 认证基础 中自定义中间件的思路:

function ensureAuthenticated(req, res, next) { if (req.isAuthenticated()) { return next(); } res.redirect("/log-in"); } app.use("/posts", ensureAuthenticated); app.use("/users", ensureAuthenticated);

更优雅的做法是把当前用户写入res.locals.currentUser(一个位于 passport 中间件之后、路由之前的应用级中间件),这样所有视图都能直接访问登录用户,无需在控制器里逐个传递。

2. 用户能通过你选择的认证方式登录

见下方"认证方案选型"一节。无论选哪种策略,登录成功后都应重定向到动态流页面,失败则回到登录页并显示错误消息(failureMessage: true后通过req.session.messages读取)。

3. 用户可以向其他用户发送关注请求

这需要一个记录"发起者 → 目标用户 → 状态(pending/accepted)"的关联模型。你的用户索引页应只对"未关注、且没有待处理请求"的用户显示关注按钮——这要求查询时排除已关注者与已发请求者。

4. 用户可以创建帖子(先只支持纯文本)

最小可行版是一个Post模型(内容 + 作者 + 时间戳)加一个发帖表单。表单 POST 到控制器,经express-validator校验后写入数据库,再按 PRG 模式重定向回动态流。

5. 用户可以点赞帖子

点赞是"用户-帖子"的多对多关系。需要注意幂等性:同一用户对同一帖子只能有一个赞,重复点击应取消点赞(toggle)或禁止重复创建。可以在数据库中为(userId, postId)建立唯一约束来兜底。

6. 用户可以评论帖子

Comment模型关联用户与帖子。评论表单紧跟在帖子详情之下,与点赞一样需要校验与防 XSS 转义。

7. 帖子应始终显示内容、作者、评论与点赞

这是查询层面的要求:获取帖子时必须"预加载"(eager load)作者、评论与点赞计数。Prisma 中对应include

const posts = await prisma.post.findMany({ include: { author: true, comments: true, likes: true, }, orderBy: { createdAt: "desc" }, });

原生 SQL 则对应JOIN与聚合查询。视图渲染时统一展示这四个要素,是保持体验一致的基础。

8. 帖子索引页:显示当前用户及其关注者的近期帖子

这是社交平台的核心——"动态流"(feed)。查询条件为:帖子作者 ∈(当前用户 ∪ 当前用户已接受的关注对象)。这是本项目最复杂的查询之一,也是数据建模是否合理的最好检验。

9. 用户可以创建带头像的资料页

资料页包含资料信息、头像与帖子列表。头像来源取决于认证方式:

  • 若用passport-github2认证,可直接复用 GitHub 账号的现有头像;
  • 否则可使用 Gravatar(通过邮箱生成头像);
  • 也可以在注册表单中直接提供头像 URL 字段。

10. 用户索引页:展示所有用户,并显示关注按钮

遍历所有用户,对每个用户根据当前登录状态决定渲染什么:

  • 已关注 → 显示"已关注"(可提供取消关注);
  • 有待处理请求 → 显示"请求已发送";
  • 未关注且无请求 → 显示"发送关注请求"按钮;
  • 自己 → 不显示任何按钮。

11. 部署到你选择的托管平台

见下方"部署上线"一节。

认证方案选型:OAuth 与本地策略

原文档明确建议:理想情况下用 passport.js 支持"通过你所克隆的社交平台进行认证"(即 OAuth 单点登录)。但部分平台(例如 Facebook)近期已让这一流程变得不可行。如果遇到这种情况,官方认可的备选方案有两个:

方案适用场景说明
passport-local通用、最基础用户名 + 密码,配合 bcrypt 哈希存储,参见 认证基础 的完整实现
passport-github2有 GitHub 账号的用户OAuth 流程,用户授权后回调,可直接复用其 GitHub 头像作为资料头像

选择passport-local时,把 认证基础 中演示的单文件结构拆分到routes/controllers/与独立的认证配置模块中。如果希望会话持久化到数据库(而非内存),可参考该课作业中提到的connect-pg-simple(PostgreSQL)或prisma-session-store(Prisma,见 文件上传项目 的用法)——注意会话表不会自动创建,需按文档手动建表。

进阶功能:超出清单的部分

原文档明确指出,聊天、实时更新、通知等"你可能没接触过的功能"不属于责任范围——除非你对当前水平非常有信心。例如socket.io可以利用 WebSocket 实现实时通信。这些功能建议放到核心功能全部完成后,作为挑战项或加分项处理。

如果你决定冲刺这些功能,仓库中的 文件上传项目 提供了可迁移的思路:先用 multer 处理上传,再把文件交给 Cloudinary 或 Supabase Storage 托管,数据库只存 URL 而非二进制数据

额外加分项(Extra credit)

核心功能完成后,原文档提供了四条加分建议,按性价比排序:

  1. 让帖子支持图片(URL 或上传)。参考 文件上传项目:Cloudinary 与 Supabase Storage 都是托管用户上传图片的好选择,它们返回的 URL 存入数据库即可。这也是你在该项目中学过的模式。
  2. 允许用户更新自己的头像——资料编辑页加一个头像字段。
  3. 访客登录功能:允许访客不经注册直接进入。这对求职简历上的项目尤其重要——大多数招聘官、HR 不会花时间注册账号。这个功能让评审者绕过繁琐注册流程直接体验你的作品。
  4. 美化界面:投入多少精力做样式完全由你决定,但一个整洁的界面会让作品完成度显著提升。

部署上线

动态网站需要能运行 Node.js 服务端代码和数据库的托管平台,GitHub Pages 这类静态托管无法胜任(详见 部署课程)。推荐的 PaaS 方案组合(按该课程的建议):

  • Railway:可同时部署服务器与数据库,按用量付费,有一次性免费额度;
  • Render:可部署服务器与数据库,每月有免费运行时长,应用闲置 15 分钟后会自动休眠;
  • Neon / Aiven:专门托管数据库的服务,均提供无需信用卡的免费层级(PostgreSQL)。

部署时注意:

  • 端口:使用process.env.PORT,托管平台会自动注入;
  • 环境变量:数据库连接串、会话密钥等凭据绝不能硬编码在代码里,通过环境变量注入;
  • Node 版本:可在package.jsonengines字段声明兼容版本,防止平台默认版本与你的代码不兼容;
  • 排错:部署失败先查构建日志,部署成功后遇到 500 页则查应用日志并复现问题;如果最新部署破坏了原本正常的应用,用git loggit checkout回退到上一个可用版本,再逐步恢复变更。

部署完成后,把线上地址分享出去——这是你整个 NodeJS 学习阶段的成果展示。

结语:从需求到上线,你已具备全栈能力

Odin Book 是你把"相对高层的需求集"转化为"可用网站"的完整演练。它检验的不只是编码能力,更是规划能力、范围管理能力和自主查阅文档的能力。正如 结课总结 所说:完成这个项目后,你构建的 Node.js 应用已经具备现实世界真正有用的功能,可以把自己视为一名"全栈开发者"了。

动手之前,再读一遍原文档最朴实也最重要的忠告:先把计划写下来,先完成核心功能,再谈其他。祝编码顺利。

【免费下载链接】curriculumThe open curriculum for learning web development项目地址: https://gitcode.com/GitHub_Trending/cu/curriculum

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询