楼层: 首页/ 软件技术/ Node.js 全栈实战/ 小项目实战:博客 API
11

小项目实战:博客 API

Project · Blog API with Express + Prisma + JWT

前面十章都是零件,这一章把它们拼成一台真车:用 Express + Prisma + SQLite + JWT 写一个博客后端——用户注册登录、文章增删改查、评论、分页、搜索。SQLite 零配置,你本地跟着敲一遍就能跑。

第一步:搭骨架

mkdir blog-api && cd blog-api npm init -y npm i express prisma @prisma/client bcryptjs jsonwebtoken dotenv npm i -D nodemon npx prisma init --datasource-provider sqlite

prisma/schema.prisma —— 三张表:用户、文章、评论

model User { id Int @id @default(autoincrement()) email String @unique name String password String // 存的是 bcrypt 哈希,不是明文 posts Post[] comments Comment[] } model Post { id Int @id @default(autoincrement()) title String content String createdAt DateTime @default(now()) authorId Int author User @relation(fields: [authorId], references: [id]) comments Comment[] } model Comment { id Int @id @default(autoincrement()) content String postId Int post Post @relation(fields: [postId], references: [id]) userId Int user User @relation(fields: [userId], references: [id]) }
$ npx prisma db push $ npx prisma generate

第二步:注册与登录(JWT)

// auth.js —— 注册、登录两个接口 import express from "express"; import bcrypt from "bcryptjs"; import jwt from "jsonwebtoken"; import { prisma } from "./prisma.js"; const router = express.Router(); // POST /auth/register router.post("/register", async (req, res) => { const { email, name, password } = req.body; const hash = await bcrypt.hash(password, 10); // 密码哈希后再存 const user = await prisma.user.create({ data: { email, name, password: hash } }); res.json({ id: user.id, email: user.email }); }); // POST /auth/login —— 验证密码,发 JWT router.post("/login", async (req, res) => { const { email, password } = req.body; const user = await prisma.user.findUnique({ where: { email } }); if (!user) return res.status(401).json({ error: "用户不存在" }); const ok = await bcrypt.compare(password, user.password); if (!ok) return res.status(401).json({ error: "密码错了" }); // 签发 token:里面塞用户 id,有效期 7 天 const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET, { expiresIn: "7d" }); res.json({ token }); }); export default router;

第三步:文章 CRUD + 分页 + 搜索

// posts.js —— 文章接口,列表支持分页和关键词搜索 import express from "express"; import { prisma } from "./prisma.js"; import { auth } from "./auth-middleware.js"; const router = express.Router(); // GET /posts?page=1&size=10&keyword=node router.get("/", async (req, res) => { const page = Number(req.query.page) || 1; const size = Number(req.query.size) || 10; const keyword = req.query.keyword || ""; const posts = await prisma.post.findMany({ where: { title: { contains: keyword } }, // 按标题模糊搜 skip: (page - 1) * size, // 跳过前面几页 take: size, // 取这一页的数量 orderBy: { createdAt: "desc" }, include: { author: { select: { name: true } } } }); res.json({ page, size, posts }); }); // POST /posts —— 只有登录用户能发文章(挂上 auth 中间件) router.post("/", auth, async (req, res) => { const post = await prisma.post.create({ data: { ...req.body, authorId: req.user.userId } }); res.status(201).json(post); }); export default router;

第四步:app.js 总装

import express from "express"; import "dotenv/config"; import authRouter from "./auth.js"; import postsRouter from "./posts.js"; const app = express(); app.use(express.json()); app.use("/auth", authRouter); app.use("/posts", postsRouter); app.listen(3000, () => console.log("博客 API 跑在 3000"));
记
这个项目教给你的

① 一个真实后端的骨架就是:鉴权(JWT)→ 路由(Router)→ ORM 查询 → 返回 JSON。

② bcrypt 存密码、JWT 换 token、Prisma 查数据、分页用 skip/take——全是生产同款写法。

③ 把它跑通、加上评论接口和编辑删除,你就具备了独立写一个 CRUD 后端的能力。