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 后端的能力。