CodeRoadMap
路线图学习路径文章题库资源社区

浏览

首页路线图学习路径知识库题库文章资源社区我的学习
CodeRoadMap

中文编程学习导航:路线图、讲义与题库,进度可同步。

路线图学习路径文章题库社区

© 2026 CodeRoadMap

津ICP备2026012044号-1|coderoadmap@126.com
首页/全栈 Web 开发/数据库

课程目录

  1. 01HTML & CSS 基础
  2. 02JavaScript 核心
  3. 03JavaScript 练习
  4. 04React 入门
  5. 05React 项目
  6. 06Node.js 与 API
  7. 07数据库
  8. 08全栈毕业项目
第 07 课

数据库

SQL 基础、ORM、认证与 CRUD 应用。

课程内容

数据库

内存里的数据重启就没了——真实应用需要数据库持久化。本课学习 SQL 基础、在 Node.js 中操作数据库,以及带认证的完整 CRUD。

学习目标

  • 会写常用的 SQL(增删改查、条件、关联)
  • 理解关系型数据库的表设计与外键
  • 在 Node.js 中用 ORM/驱动操作数据库
  • 实现注册登录(密码哈希 + JWT)

SQL 速览

-- 建表
CREATE TABLE users (
  id INT PRIMARY KEY AUTO_INCREMENT,
  username VARCHAR(50) UNIQUE NOT NULL,
  password_hash VARCHAR(255) NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE todos (
  id INT PRIMARY KEY AUTO_INCREMENT,
  user_id INT NOT NULL,
  title VARCHAR(200) NOT NULL,
  done BOOLEAN DEFAULT FALSE,
  FOREIGN KEY (user_id) REFERENCES users(id)  -- 外键关联
);

-- 增
INSERT INTO todos (user_id, title) VALUES (1, '学 SQL');

-- 查(带条件和关联)
SELECT t.title, u.username
FROM todos t JOIN users u ON t.user_id = u.id
WHERE t.done = FALSE
ORDER BY t.id DESC
LIMIT 10;

-- 改 / 删
UPDATE todos SET done = TRUE WHERE id = 1;
DELETE FROM todos WHERE id = 1;

Node.js 连接数据库

以 MySQL 为例(用连接池,不要每次请求新建连接):

const mysql = require("mysql2/promise");

const pool = mysql.createPool({
  host: "localhost",
  user: "root",
  password: process.env.DB_PASSWORD, // 密码放环境变量!
  database: "todo_app",
});

// 查询(永远用参数占位符 ?,防 SQL 注入)
app.get("/api/todos", async (req, res) => {
  const [rows] = await pool.query(
    "SELECT * FROM todos WHERE user_id = ?",
    [req.userId]
  );
  res.json(rows);
});

安全红线:绝不把用户输入直接拼进 SQL 字符串,必须用参数化查询。

认证:注册与登录

const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");

// 注册:密码哈希后存储(绝不存明文!)
app.post("/api/register", async (req, res) => {
  const { username, password } = req.body;
  const hash = await bcrypt.hash(password, 10);
  await pool.query(
    "INSERT INTO users (username, password_hash) VALUES (?, ?)",
    [username, hash]
  );
  res.status(201).json({ message: "注册成功" });
});

// 登录:校验密码,签发 JWT
app.post("/api/login", async (req, res) => {
  const { username, password } = req.body;
  const [rows] = await pool.query(
    "SELECT * FROM users WHERE username = ?", [username]
  );
  const user = rows[0];
  if (!user || !(await bcrypt.compare(password, user.password_hash))) {
    return res.status(401).json({ error: "用户名或密码错误" });
  }
  const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET, {
    expiresIn: "7d",
  });
  res.json({ token });
});

// 认证中间件:保护需要登录的接口
function auth(req, res, next) {
  try {
    const token = req.headers.authorization;
    req.userId = jwt.verify(token, process.env.JWT_SECRET).userId;
    next();
  } catch {
    res.status(401).json({ error: "请先登录" });
  }
}

app.get("/api/todos", auth, async (req, res) => { /* ... */ });

动手练习

  1. 把上一课的 Todo API 接入 MySQL:todos 表 + users 表,外键关联。
  2. 加注册/登录,未登录用户访问 /api/todos 返回 401。
  3. 每个用户只能看到自己的 todo(查询都带 user_id 条件)。

参考资源

  • MySQL 教程(菜鸟)
  • SQLBolt — 交互式 SQL 练习
  • jsonwebtoken 文档

观看视频课程

https://www.theodinproject.com/paths/foundations/courses/foundations

← 上一课Node.js 与 API下一课 →全栈毕业项目