[Series Thực Chiến E-commerce] Bài 21: "Phân lô" kho hàng - Tạo Model & API Thêm Danh Mục (Category)
Chào anh em!
Sau chuỗi ngày vật lộn với logic của mảng Sản phẩm (Thêm, sửa, xóa, lọc, phân trang, đánh giá...), kho hàng của chúng ta hiện tại đang khá lộn xộn. Điện thoại, ốp lưng, sạc cáp, tai nghe... đang bị vứt chung vào một đống. Khách hàng vào web nhìn thấy một mớ bòng bong chắc chắn sẽ thoát trang ngay lập tức.
Để giải quyết vấn đề này, hệ thống cần có tính năng Phân loại danh mục (Category). Mỗi sản phẩm khi tạo ra (ở Bài 14) đều có một trường category tham chiếu đến danh mục mà nó thuộc về. Hôm nay, chúng ta sẽ xây dựng nền móng cho module Danh mục này: Tạo Model và viết API Thêm mới Danh mục nhé!
1. Đúc khuôn Danh mục (Model)
Thông tin của một danh mục thường rất đơn giản, cốt lõi nhất chỉ là cái tên (ví dụ: "Điện thoại", "Laptop", "Phụ kiện").
Anh em tạo file models/productCategory.js và định nghĩa Schema như sau:
const mongoose = require('mongoose');
var ProductCategorySchema = new mongoose.Schema({
title: {
type: String,
required: true,
unique: true, // 💡 Tránh trùng lặp (vd: Không thể có 2 danh mục "Điện thoại")
index: true, // 💡 Đánh index để tốc độ query tìm kiếm danh mục nhanh hơn
},
}, {
timestamps: true // Tự động có createdAt, updatedAt
});
module.exports = mongoose.model('ProductCategory', ProductCategorySchema);
Góp ý thực chiến: Ở các dự án thực tế quy mô lớn hơn, anh em có thể cân nhắc thêm trường slug (giống bên Product) để làm URL thân thiện với SEO cho trang danh mục, hoặc thêm tham chiếu parent_id nếu làm danh mục đa cấp (Danh mục cha - Danh mục con). Nhưng với series này, để anh em dễ hiểu luồng đi, mình sẽ giữ nó tinh gọn nhất có thể.
2. Não bộ xử lý (Controller)
Có khuôn rồi thì viết hàm để nhét data vào thôi. Anh em mở (hoặc tạo mới) file controllers/productCategory.js:
const ProductCategory = require('../models/productCategory');
const asyncHandler = require('express-async-handler');
const createCategory = asyncHandler(async (req, res) => {
// Validate cơ bản: Bắt buộc phải nhập tên danh mục
if (!req.body.title) {
return res.status(400).json({
success: false,
message: 'Title is required - Vui lòng nhập tên danh mục'
});
}
// Quăng data vào DB để tạo mới
const response = await ProductCategory.create(req.body);
// 💡 ĐIỂM SÁNG RESTful API: Trả về status 201 thay vì 200
return res.status(201).json({
success: true,
createdCategory: response
});
});
module.exports = {
createCategory,
};
Tại sao lại là 201? Theo chuẩn HTTP Status Code, 200 OK dùng cho các request thành công chung chung. Nhưng khi bạn thực hiện một request POST dẫn đến việc tạo ra một tài nguyên mới trên server, mã 201 Created là chuẩn xác nhất. Nó giúp Frontend (hoặc các hệ thống bên thứ 3 gọi API của bạn) phân biệt rõ ràng hành vi vừa diễn ra.
3. Thiết lập Trạm gác (Router)
Ai là người được phép thêm mới danh mục vào hệ thống? Chắc chắn không phải là user bình thường hay khách vãng lai rồi. Chỉ có sếp (Admin) mới có quyền "phân lô" kho hàng thôi.
Chúng ta lại mời hai anh bảo vệ quen thuộc ra gác cổng. Tạo file routers/productCategory.js:
const router = require('express').Router();
const ctrls = require('../controllers/productCategory');
const { verifyAccessToken, isAdmin } = require('../middlewares/verifyToken');
// Tuyến đường POST yêu cầu: Đăng nhập + Quyền Admin
router.post('/categories', [verifyAccessToken, isAdmin], ctrls.createCategory);
module.exports = router;
Lưu ý nhỏ nhưng hay quên: Mở tuyến đường mới thì phải báo cho trạm điều phối trung tâm biết. Anh em nhớ vào file routers/index.js để nhúng cái productCategory router này vào nhé (ví dụ: app.use('/api/category', productCategoryRouter)).
Lời kết
Bật Postman lên test thử nào! Kẹp cái Token của Admin vào Header, bắn request POST kèm JSON {"title": "Điện thoại"}. Nếu nhận về Status 201 cùng object data báo thành công là anh em đã hoàn thành xuất sắc nhiệm vụ.
Có danh mục rồi, bước tiếp theo khi hiển thị giao diện trang chủ cho khách hàng, Frontend sẽ gọi API để lấy toàn bộ danh sách các danh mục này ra để show lên thanh Menu.
Và đó cũng chính là nhiệm vụ của bài tiếp theo: Lession 22: Get All Categories. Hẹn gặp lại anh em ở bài sau nhé!
All rights reserved