[Series Thực Chiến E-commerce] Bài 17: "Dọn kho" dứt khoát - Xóa Sản Phẩm (Delete Product) & Lỗi Copy-Paste kinh điển
Chào anh em!
Vậy là chúng ta đã đi qua các bước Thêm (Create), Đọc (Read), và Sửa (Update) thông tin sản phẩm rồi. Mảnh ghép cuối cùng để hoàn thiện bộ CRUD cơ bản cho kho hàng chính là chức năng Xóa (Delete).
Thực tế thì khi làm dự án thật, người ta ít khi xóa hẳn dữ liệu (Hard Delete) mà thường dùng kỹ thuật ẩn đi (Soft Delete - thêm cờ isDeleted: true). Tuy nhiên, ở góc độ học tập và thao tác chuẩn với MongoDB, chúng ta cứ thực hành "trảm" tận gốc bằng hàm findByIdAndDelete trước cho quen tay nhé.
Đặc biệt, trong đoạn code hôm nay có một "vết gợn" siêu kinh điển mà 90% anh em dev khi code quá đà đều từng mắc phải. Cùng xem đó là gì nhé!
1. Hàm Xóa Sản Phẩm (Controller) & Pha "Tự hủy" ngầm
Dưới đây là đoạn logic lấy ID từ URL để xóa sản phẩm. Nhưng anh em nhìn kỹ xem có thấy dòng code nào bị "lạc quẻ" không?
const deleteProduct = asyncHandler(async (req, res) => {
const { pid } = req.params;
// ❌ VẾT GỢN COPY-PASTE LÀ ĐÂY:
// if (req.body && req.body.title) req.body.slug = slugify(req.body.title)
const deletedProduct = await Product.findByIdAndDelete(pid);
return res.status(200).json({
success: deletedProduct ? true : false,
deletedProduct: deletedProduct ? deletedProduct : 'Cannot delete product'
});
});
Bóc phốt xíu nhé: Cái dòng check req.body.title rồi tạo slug rõ ràng là logic của hàm Update ở Bài 16, chắc chắn lúc code anh em đã tiện tay copy-paste sang đây rồi quên xóa đi đúng không? 😂
Bản chất của phương thức DELETE là nó chỉ mang theo Params (cái pid trên URL), hiếm khi nào (và không nên) nhét data vào req.body. Hành động xóa thì chỉ cần biết "Xóa cái gì" (ID), chứ quan tâm gì đến việc nó có title mới hay không để mà sinh slug? Để dòng này lại code vẫn chạy, nhưng nó là code thừa, chạy lãng phí tài nguyên máy chủ.
Code chuẩn "sạch bong kin kít" sẽ như thế này:
const deleteProduct = asyncHandler(async (req, res) => {
const { pid } = req.params;
// Gọi đội Mongoose ra thi hành án
const deletedProduct = await Product.findByIdAndDelete(pid);
return res.status(200).json({
success: !!deletedProduct,
deletedProduct: deletedProduct || 'Cannot delete product - Xóa thất bại'
});
});
module.exports = {
createProduct,
getProduct,
updateProduct,
deleteProduct // Bưng ra ngoài
};
2. Cắm chốt ở Router
Cũng giống như Xóa User, hành động Xóa Product làm bốc hơi tài sản của công ty, nên bắt buộc phải có thẻ Admin mới được gọi API này.
Anh em mở routers/product.js ra và lắp thêm trạm gác vào:
const express = require('express');
const router = express.Router();
const ctrls = require('../controllers/product');
const { verifyAccessToken, isAdmin } = require('../middlewares/verifyToken');
// ... các route cũ
// Route Delete: Admin only!
router.delete('/:pid', [verifyAccessToken, isAdmin], ctrls.deleteProduct);
module.exports = router;
Test trên Postman: Bắn request DELETE vào http://localhost:5000/api/product/id-san-pham, nhớ nhét token của Admin vào Header. Kết quả trả về thông tin sản phẩm vừa bị xóa là anh em đã thành công dọn rác kho hàng rồi.
Lời kết
Vậy là module Admin xử lý sản phẩm cơ bản đã xong. Nhẹ nhàng tình cảm đúng không?
Nhưng kho hàng có đến hàng ngàn sản phẩm, làm sao khách hàng tìm được món họ cần? Khách muốn xem "Điện thoại giá dưới 10 triệu", "Sắp xếp theo giá từ thấp đến cao", hoặc "Sang trang 2, trang 3"... thì Backend phải xử lý thế nào?
Đó chính là lúc chúng ta chạm trán với Lession 18: Filtering, sorting & pagination products. Đây là một bài cực kỳ "khoai", đòi hỏi tư duy logic cao, nhưng cũng là phần hay nhất để thể hiện trình độ Backend của anh em.
All rights reserved