
กำลังโหลด...

กำลังโหลด...

Login สำเร็จยังไม่ได้แปลว่าแก้หนังสือของทุกคนได้
เราจะตรวจ token ก่อน แล้วบังคับ user_id ใน SQL ให้ตรงกับคนที่ล็อกอิน
index.js เดิมทีละขั้นMission นี้ต่อจาก books-api ที่ทำมาแล้ว จึงใช้ไฟล์เดียวคือ index.js เหมือน Chapter 3 และ 4 ไม่ต้องสร้าง middleware/ หรือ booksRouter ใหม่
flowchart LR
A["DELETE /books/7\n+ Bearer token"] --> B{"token ถูกไหม?"}
B -->|"ไม่"| C["401 Unauthorized"]
B -->|"ใช่"| D["jwt.verify → req.user.userId"]
D --> E{"book.user_id ตรงกันไหม?"}
E -->|"ไม่"| F["404 ไม่ลบ"]
E -->|"ใช่"| G["DELETE + RETURNING *"]
| ของ | มาจาก Chapter ไหน | เช็กอย่างไร |
|---|---|---|
pool |
Chapter 3 | มี import { pool } from "./db.js"; |
jwt |
Chapter 4 | มี import jwt from "jsonwebtoken"; |
JWT_SECRET |
Chapter 3 | อยู่ใน .env |
| token | Chapter 4 | Login สำเร็จแล้วได้ { "token": "..." } |
index.jsไม่ต้อง import ซ้ำ ถ้ามีอยู่แล้วให้ข้าม:
import "dotenv/config";
import express from "express";
import jwt from "jsonwebtoken";
import { pool } from "./db.js";
verifyToken ใน index.jsวาง function นี้ หลัง app.use(express.json()) และ ก่อน route /books ทั้งหมด:
function verifyToken(req, res, next) {
const header = req.headers.authorization;
const token = header?.startsWith("Bearer ")
? header.split(" ")[1]
: null;
if (!token) {
return res.status(401).json({ message: "Token required" });
}
try {
req.user = jwt.verify(token, process.env.JWT_SECRET);
next();
} catch {
return res.status(401).json({ message: "Invalid token" });
}
}
verifyToken ทำอะไรAuthorization header → token → jwt.verify → req.user → next()
Bearer ผิด → หยุดที่ 401 Token required401 Invalid tokenreq.user.userId แล้ว Request จึงไป route ถัดไปหา app.delete("/books/:id", ...) ตัวเดิมที่ยังใช้ readBooks() และ writeBooks() แล้ว แทนที่ทั้งก้อน ด้วยโค้ดนี้:
app.delete("/books/:id", verifyToken, async (req, res) => {
try {
const result = await pool.query(
`DELETE FROM books
WHERE id = $1 AND user_id = $2
RETURNING *`,
[req.params.id, req.user.userId]
);
if (result.rows.length === 0) {
return res.status(404).json({ message: "Book not found" });
}
res.json({ message: "ลบสำเร็จ", book: result.rows[0] });
} catch (error) {
console.error(error);
res.status(500).json({ error: "Database query failed" });
}
});
| โค้ด | หน้าที่ |
|---|---|
verifyToken หลัง URL |
บังคับให้ผ่านการตรวจ token ก่อนเข้า route |
req.params.id |
id หนังสือจาก URL เช่น /books/7 |
req.user.userId |
id เจ้าของจาก token ที่ตรวจแล้ว |
WHERE id = $1 AND user_id = $2 |
ต้องตรงทั้งหนังสือและเจ้าของ จึงลบได้ |
RETURNING * |
ส่ง row ที่ลบสำเร็จกลับมาเป็นหลักฐาน |
หลัง Register ให้เปิด pgAdmin Query Tool และดู id ของ user:
SELECT id, email FROM users;
สร้างหนังสือที่มีเจ้าของ (เปลี่ยน 1 เป็น id ของ user ที่ Login):
INSERT INTO books (title, author, year, user_id)
VALUES ('Book for token test', 'Student', 2026, 1)
RETURNING *;
จด id ของ book ที่เพิ่งสร้างไว้ เช่น 7
POST /auth/login แล้ว copy ค่า token จาก ResponseDELETE http://localhost:3000/books/7Key: Authorization
Value: Bearer วาง-token-ที่-copy-มา
| ลองแบบไหน | สิ่งที่ต้องเห็น |
|---|---|
| ไม่ใส่ Header | 401 Token required |
| ใส่ token ปลอม | 401 Invalid token |
| token ของคนอื่น | 404 Book not found และข้อมูลยังอยู่ |
| token ของเจ้าของ | 200 และ { message, book } |
Authentication บอกว่า “คุณคือใคร”; Authorization บอกว่า “รายการนี้เป็นของคุณหรือไม่”

ทำใน index.js ของ books-api
import jwt from "jsonwebtoken";
import { pool } from "./db.js";
วาง function verifyToken หลัง app.use(express.json()) และก่อน route /books
แทนที่ app.delete("/books/:id", ...) เดิมทั้งก้อน แล้วเติม TODO สองตำแหน่ง:
function verifyToken(req, res, next) {
const header = req.headers.authorization;
const token = header?.startsWith("Bearer ")
? header.split(" ")[1]
: null;
if (!token) return res.status(401).json({ message: "Token required" });
try {
req.user = jwt.TODO(token, process.env.JWT_SECRET);
next();
} catch {
res.status(401).json({ message: "Invalid token" });
}
}
app.delete("/books/:id", verifyToken, async (req, res) => {
const result = await pool.query(
`DELETE FROM books
WHERE id = $1 AND user_id = $2
RETURNING *`,
[req.params.id, req.user.TODO]
);
if (result.rows.length === 0) {
return res.status(404).json({ message: "Book not found" });
}
res.json({ message: "ลบสำเร็จ", book: result.rows[0] });
});
token แล้วตั้ง Header ใน Postman:Authorization: Bearer วาง-token-ที่-copy-มา
