Skip to main content

Products Module

The polymorphic product catalog – single collection, multiple types, discriminators, and Cloudinary image pipeline.

Polymorphic Cloudinary Cache


📖 Table of Contents


Base Route : /api/v1/products

Public endpoints are open to all. Admin endpoints require a valid access token with the ADMIN role.


Endpoints Overview

MethodEndpointAccessDescription
GET/PublicList products with pagination, filtering, and text search
GET/:idPublicFetch a single product by ID (populates all polymorphic fields)
POST/Admin (Bearer)Create a new product (multipart/form-data with images)
PATCH/:idAdmin (Bearer)Update product details (text fields only)
DELETE/:idAdmin (Bearer)Soft‑delete a product (sets isActive: false)

Rate limits: standardLimiter (100/15min) on all endpoints.


Polymorphic Types

Products are stored in a single MongoDB collection using Mongoose discriminators. Each type has required fields:

itemTypeRequired Fields (in addition to base)
BANGLEbangleSizes (number[]), packSize (number), material, weightGrams, isFragile
APPARELsizes (string[]), fabric, washingCare (string), gender
FABRIClengthMeters (number), widthCm (number), fabricType, pattern
INNERWEARsizes (string[]), fabric, packSize (number)
ACCESSORYmaterial, weightGrams, isFragile (boolean), dimensions (object)

All types share base fields: name, sku, description, mainCategory, subCategory, basePrice, currentStock, images, isActive, tags.


Endpoint Details

Public Endpoints

GET /

List all active products with pagination, filtering, and text search (MongoDB text index on name, description, tags).

Query parameters (optional):

ParamTypeDefaultMaxDescription
pageinteger1Page number (1‑based)
limitinteger20100Items per page
qstringText search query (searches name, description, tags)
itemTypestringFilter by product type (BANGLE, APPAREL, etc.)
mainCategorystringFilter by main category
minPricenumberMinimum base price
maxPricenumberMaximum base price
sortBystringcreatedAt:descbasePrice:asc, basePrice:desc, createdAt:desc

Example request:

GET /api/v1/products?page=2&limit=15&itemType=BANGLE&minPrice=500&sortBy=basePrice:asc

Response (200 OK):

{
"success": true,
"statusCode": 200,
"message": "Products retrieved successfully",
"data": {
"products": [
{
"_id": "64a7b...",
"itemType": "BANGLE",
"sku": "BAN-001",
"name": "Bridal Glass Bangle Set",
"mainCategory": "Bangles",
"subCategory": "Glass Bangles",
"basePrice": 450,
"discount": 10,
"currentStock": 50,
"images": ["https://res.cloudinary.com/..."],
"bangleSizes": [2.4, 2.6],
"packSize": 12,
"isActive": true
}
],
"meta": {
"total": 500,
"limit": 15,
"page": 2
}
},
"timestamp": "2026-05-22T10:30:00.000Z"
}

The response includes type‑specific fields only for the corresponding itemType.

GET /:id

Fetch a single product by its MongoDB ObjectId. Returns all fields (including polymorphic ones).

URL Parameter:

  • id – valid 24‑hex ObjectId.

Response (200 OK): Same shape as product object in the list.

Error (404): "Product not found" (if ID invalid or product soft‑deleted).

Admin Endpoints

POST /

Create a new product. Requires multipart/form-data with at least one image. The request is validated against the polymorphic schema based on itemType.

Headers:

  • Authorization: Bearer <admin_access_token>
  • Content-Type: multipart/form-data

Form fields (text):

FieldRequiredExample
itemTypeYesBANGLE
skuYesBAN-001
nameYesBridal Glass Bangle Set
descriptionYesBeautiful red glass bangles...
mainCategoryYesBangles
subCategoryYesGlass Bangles
basePriceYes450
currentStockYes50
... plus type‑specific fields (see validation table)

File field:

  • images – array of images (max 5 files, each ≤ 10MB).

Response (201 Created):

{
"success": true,
"statusCode": 201,
"message": "Product created successfully",
"data": {
"_id": "64a7b...",
"sku": "BAN-001",
"name": "Bridal Glass Bangle Set",
"images": ["https://res.cloudinary.com/..."]
},
"timestamp": "2026-05-22T10:35:00.000Z"
}

Images are uploaded to Cloudinary, converted to WebP, and stored as URLs.

PATCH /:id

Update product text fields (cannot update images via this endpoint – separate endpoint exists for appending images). All fields are optional.

Headers: - Authorization: Bearer <admin_access_token>
Content-Type: application/json

Request body (JSON):

{
"basePrice": 499,
"currentStock": 45
}

Response (200 OK): Returns the updated product.

DELETE /:id

Soft‑delete a product – sets isActive: false. The product remains in the database for order history but is hidden from public endpoints.

Headers:
Authorization: Bearer <admin_access_token>

Response (200 OK):

{
"success": true,
"statusCode": 200,
"message": "Product successfully removed from catalog",
"data": null,
"timestamp": "2026-05-22T10:40:00.000Z"
}

To permanently delete, a separate admin script is required (not exposed via API).


Validation Rules (Zod)

Base validation for all products

FieldRules
skustring, 3‑50 chars, unique
namestring, 3‑150 chars
descriptionstring, 10‑2000 chars
mainCategorystring, required
subCategorystring, required
basePricenumber ≥ 0
currentStockinteger ≥ 0
imagesarray of URLs (auto‑generated)

Type‑specific required fields (examples)

itemTypeAdditional required fields
BANGLEbangleSizes (array of numbers), packSize (integer), material, weightGrams, isFragile
APPARELsizes (array of strings: "XS", "S", "M", "L", "XL"), fabric, washingCare, gender

All schemas use .strict() – extra fields for the given itemType are rejected.


Error Responses

StatusCodeExample messageWhen
400BAD_REQUEST"Validation Failed: bangleSizes: Required for BANGLE type"Missing polymorphic required field
400BAD_REQUEST"Image file is required"No image in POST request
401UNAUTHORIZED"Authentication required"Missing/invalid token on admin route
403FORBIDDEN"You do not have permission to perform this action"Non‑admin user tries admin endpoint
404NOT_FOUND"Product not found"Invalid ID or soft‑deleted product
409CONFLICT"Product with this SKU already exists"Duplicate SKU on creation
429TOO_MANY_REQUESTS"Too many requests, please try again later."Rate limit exceeded

Runbook

For manual testing with Thunder Client / Postman, see ../../testing/product-runbook.md. The runbook covers:

  • Creating a Bangle product with image upload (multipart/form-data)
  • Fetching catalog with filters
  • Updating inventory and price
  • Soft‑deleting a product
  • Edge cases: wrong polymorphic fields, duplicate SKU, file size limits


One collection, infinite types – the Reshma‑Core polymorphic product engine.