Thực hành CRUD với mảng dữ liệu giả trong Node.js/Express
Mục tiêu
- Hiểu rõ cách thực hiện các thao tác CRUD (Create, Read, Update, Delete) với dữ liệu giả.
- Thực hành viết các endpoint API cơ bản.
- Làm quen với cách xử lý lỗi và phản hồi trạng thái HTTP.
- Chuẩn bị nền tảng để làm việc với cơ sở dữ liệu thực tế.
Yêu cầu thực hành
Tạo mảng dữ liệu giả
Tạo một mảng dữ liệu giả để lưu trữ các bài viết. Mỗi bài viết có các thuộc tính sau:
id
: số nguyên, định danh duy nhất.title
: chuỗi, tiêu đề bài viết.content
: chuỗi, nội dung bài viết.
Ví dụ:
let posts = [
{ id: 1, title: "Bài viết 1", content: "Nội dung bài viết 1" },
{ id: 2, title: "Bài viết 2", content: "Nội dung bài viết 2" },
];
Các endpoint cần thực hiện
GET /api/posts
Trả về danh sách tất cả bài viết.GET /api/posts/:id
Trả về chi tiết bài viết theoid
. Trả về 404 nếu không tìm thấy.POST /api/posts
Thêm bài viết mới. Dữ liệu gửi lên từ client quareq.body
.PUT /api/posts/:id
Cập nhật bài viết theoid
. Trả về 404 nếu không tìm thấy.DELETE /api/posts/:id
Xóa bài viết theoid
. Trả về 404 nếu không tìm thấy.
Hướng dẫn thực hành
Khởi tạo router
import express from "express";
const postRouter = express.Router();
let posts = [
{ id: 1, title: "Bài viết 1", content: "Nội dung bài viết 1" },
{ id: 2, title: "Bài viết 2", content: "Nội dung bài viết 2" },
];
// GET /api/posts - Lấy danh sách bài viết
postRouter.get("/", (req, res) => {
res.json(posts);
});
// GET /api/posts/:id - Lấy chi tiết bài viết
postRouter.get("/:id", (req, res) => {
const post = posts.find((p) => p.id === parseInt(req.params.id));
if (!post) return res.status(404).json({ error: "Post not found" });
res.json(post);
});
// POST /api/posts - Thêm bài viết mới
postRouter.post("/", (req, res) => {
const { title, content } = req.body;
const newPost = { id: Date.now(), title, content };
posts.push(newPost);
res.status(201).json(newPost);
});
// PUT /api/posts/:id - Cập nhật bài viết
postRouter.put("/:id", (req, res) => {
const post = posts.find((p) => p.id === parseInt(req.params.id));
if (!post) return res.status(404).json({ error: "Post not found" });
const { title, content } = req.body;
post.title = title || post.title;
post.content = content || post.content;
res.json(post);
});
// DELETE /api/posts/:id - Xóa bài viết
postRouter.delete("/:id", (req, res) => {
const index = posts.findIndex((p) => p.id === parseInt(req.params.id));
if (index === -1) return res.status(404).json({ error: "Post not found" });
posts.splice(index, 1);
res.json({ success: true });
});
export default router;
Tích hợp router vào ứng dụng
import express from "express";
import dotenv from "dotenv";
import postsRouter from "./routers/posts";
dotenv.config();
const app = express();
app.use(express.json()); // Middleware để parse JSON
app.use("/api/posts", postsRouter);
app.listen(process.env.PORT, () => {
console.log(`Server is running on port ${process.env.PORT}`);
});
Bài tập nâng cao
Tìm kiếm:
Thêm endpoint
GET /api/posts?search=keyword
để tìm kiếm bài viết theo tiêu đề.Nếu có tham số
search
, trả về danh sách bài viết có tiêu đề chứa từ khóa tìm kiếm (không phân biệt hoa thường).Nếu không có tham số
search
, trả về tất cả bài viết.Nếu không tìm thấy bài viết nào, trả về danh sách rỗng.
Nếu có lỗi xảy ra, trả về mã trạng thái 500 và thông báo lỗi.
Nếu không có bài viết nào, trả về mã trạng thái 404 và thông báo lỗi.
Gợi ý:
javascript// GET /api/posts?search=keyword router.get("/", (req, res) => { try { const { search } = req.query; if (search) { const filteredPosts = posts.filter((post) => post.title.toLowerCase().includes(search.toLowerCase()) ); if (filteredPosts.length === 0) { return res.status(404).json({ error: "No posts found" }); } return res.json(filteredPosts); } if (posts.length === 0) { return res.status(404).json({ error: "No posts available" }); } return res.json(posts); } catch (error) { return res.status(500).json({ error: "Server error", message: error.message }); } });
Nếu có thắc mắc, đừng ngại hỏi thầy hoặc các bạn nhé!
Chúc các em học tốt! 🚀 — Thầy Đạt 🧡