Design and implement a RESTful Express API: route structure, validation, error handling, pagination, and OpenAPI documentation
From mern-stacknpx claudepluginhub chavangorakh1999/sde-skills --plugin mern-stack[resource or domain, e.g. 'blog posts with comments and tags']Design and implement a complete, production-quality REST API.
Define the resource hierarchy:
Standard RESTful pattern:
GET /resources — list with pagination
POST /resources — create
GET /resources/:id — get one
PUT /resources/:id — replace (rare)
PATCH /resources/:id — partial update
DELETE /resources/:id — delete
Write Joi schemas for all request bodies and query parameters:
const objectIdSchema = Joi.string().regex(/^[0-9a-fA-F]{24}$/).required();
Implement the layered architecture:
Routes — register middleware, delegate to controller
router.get('/', validate(listSchema, 'query'), asyncHandler(controller.list));
Controller — HTTP layer only, no business logic
async list(req, res) {
const result = await service.list(req.query, req.user);
res.json({ data: result.items, pagination: result.pagination });
}
Service — all business logic, no HTTP concepts
Implement cursor-based pagination for production, offset for simplicity:
Offset (simple, less efficient):
const { page = 1, limit = 20 } = query;
const [items, total] = await Promise.all([
Model.find(filter).sort(sort).skip((page - 1) * limit).limit(limit).lean(),
Model.countDocuments(filter)
]);
return { items, pagination: { page, limit, total, totalPages: Math.ceil(total / limit) } };
Cursor-based (scalable):
Use _id or a sorted field as cursor, return nextCursor in response.
Standardize all responses:
// Success list:
{ "data": [...], "pagination": { "page": 1, "limit": 20, "total": 100 } }
// Success single:
{ "data": { "id": "...", ... } }
// Error:
{ "error": { "code": "VALIDATION_ERROR", "message": "...", "details": [...] } }
Apply to all routes:
express.json({ limit: '10kb' })Generate API spec:
# Document each endpoint with:
paths:
/resources:
get:
summary: List resources
parameters: [page, limit, filter params]
responses:
200: { description: Success, schema: ... }
401: { description: Unauthorized }
Use swagger-jsdoc + swagger-ui-express to serve docs at /api/docs.