Tạo một CRUD API endpoint mới theo chuẩn kiến trúc project AR (Model → Validation → Service → Controller → Route). Sử dụng skill này mỗi khi cần tạo mới một resource API.
Khi tạo một API endpoint mới cho project, BẮT BUỘC tuân theo kiến trúc layered này:
Request → Route → [Validation Middleware] → Controller → Service → Model → MongoDB
src/models/<ResourceName>.js){ timestamps: true } để tự tạo createdAt, updatedAtmongoose.model()MenuItem.js, Table.js)// Ví dụ mẫu:
import mongoose from 'mongoose';
const resourceSchema = new mongoose.Schema({
name: { type: String, required: true },
// ... các fields khác
}, { timestamps: true });
export default mongoose.model('Resource', resourceSchema);
src/validations/<resource>Validation.js)body, params, query trong một objectorderValidation.js)// Ví dụ mẫu:
import { z } from 'zod';
export const createResourceSchema = z.object({
body: z.object({
name: z.string().min(1, 'Name is required'),
// ... các fields khác
})
});
export const updateResourceSchema = z.object({
params: z.object({
id: z.string().regex(/^[0-9a-fA-F]{24}$/, 'Invalid ID')
}),
body: z.object({
// ... fields cần update
})
});
src/services/<resource>Service.js)orderService.js)// Ví dụ mẫu:
import Resource from '../models/Resource.js';
export const createResource = async (data) => {
const resource = new Resource(data);
return await resource.save();
};
export const getAllResources = async (filter = {}) => {
return await Resource.find(filter).sort({ createdAt: -1 });
};
export const getResourceById = async (id) => {
const resource = await Resource.findById(id);
if (!resource) throw new Error('Resource not found');
return resource;
};
export const updateResource = async (id, data) => {
const updated = await Resource.findByIdAndUpdate(id, data, { new: true });
if (!updated) throw new Error('Resource not found');
return updated;
};
export const deleteResource = async (id) => {
const deleted = await Resource.findByIdAndDelete(id);
if (!deleted) throw new Error('Resource not found');
return deleted;
};
src/controllers/<resource>Controller.js)express-async-handler để wrap async functions{ message: '...', data: ... }req.io// Ví dụ mẫu:
import asyncHandler from 'express-async-handler';
import * as resourceService from '../services/resourceService.js';
export const getAll = asyncHandler(async (req, res) => {
const items = await resourceService.getAllResources(req.query);
res.status(200).json({ message: 'Fetched successfully', data: items });
});
export const getById = asyncHandler(async (req, res) => {
const item = await resourceService.getResourceById(req.params.id);
res.status(200).json({ message: 'Fetched successfully', data: item });
});
export const create = asyncHandler(async (req, res) => {
const item = await resourceService.createResource(req.body);
// Emit socket event nếu cần realtime
req.io.to('admin').emit('resource:new', item);
res.status(201).json({ message: 'Created successfully', data: item });
});
export const update = asyncHandler(async (req, res) => {
const item = await resourceService.updateResource(req.params.id, req.body);
res.status(200).json({ message: 'Updated successfully', data: item });
});
export const remove = asyncHandler(async (req, res) => {
await resourceService.deleteResource(req.params.id);
res.status(200).json({ message: 'Deleted successfully' });
});
src/routes/<resource>Routes.js)validateParams từ ../middlewares/validateRequest.jsrouterRoutes (VD: orderRoutes.js)// Ví dụ mẫu:
import express from 'express';
import { validateParams } from '../middlewares/validateRequest.js';
import { createResourceSchema, updateResourceSchema } from '../validations/resourceValidation.js';
import * as resourceController from '../controllers/resourceController.js';
const router = express.Router();
router.get('/', resourceController.getAll);
router.get('/:id', resourceController.getById);
router.post('/', validateParams(createResourceSchema), resourceController.create);
router.patch('/:id', validateParams(updateResourceSchema), resourceController.update);
router.delete('/:id', resourceController.remove);
export default router;
src/server.js)/api/v1/<resource-name>import resourceRoutes from './routes/resourceRoutes.js';
app.use('/api/v1/resources', resourceRoutes);
timestamps: trueserver.js