Catalog Product API & Integration Guide
HTTP contracts for catalog product admin and customer/mobile endpoints.
Catalog Product - API & Integration Guide
1. How to Read / Quick Metadata
- Module: Catalog / Product
- Admin auth:
JwtAuthGuard+RoleGuard+@Permissions(Products_*) - Customer/mobile auth: public endpoints (
@Public()) - Surfaces:
- Admin:
/api/admin/products - Customer:
/api/products - Mobile:
/api/mobile/products
- Admin:
Audience: Mobile/web frontend developers Scope: Product discovery, detail endpoints
2. High-Level Overview
Product APIs provide admin lifecycle management and public browse/detail surfaces for published sellable products. Product detail accepts historical slugs and resolves to the current canonical product.
3. Core Concepts and Terminology
- Sellable visibility: customer APIs only return
isSellable=trueandstatus=published. - Canonical slug: current slug returned in payload.
- Historical slug: old slug stored in
product_slug_historyand still resolvable. - Featured list endpoint: curated subset exposed by
GET /products/featured. - Availability endpoint: live stock status exposed by
GET /products/:slug/availability(uncached for UI stock correctness).
4. Route Summary
Admin
| Method | Path | Permission |
|---|---|---|
GET | /api/admin/products | Products_READ |
GET | /api/admin/products/:id | Products_READ |
POST | /api/admin/products | Products_CREATE |
PATCH | /api/admin/products/:id | Products_UPDATE |
DELETE | /api/admin/products/:id | Products_DELETE |
Customer + Mobile
| Method | Path |
|---|---|
GET | /api/products and /api/mobile/products |
GET | /api/products/featured and /api/mobile/products/featured |
GET | /api/products/:slug and /api/mobile/products/:slug |
GET | /api/products/:slug/availability and /api/mobile/products/:slug/availability |
Route Details
List Products
| Aspect | Details |
|---|---|
| Endpoint | GET /api/products or /api/mobile/products |
| Auth | Public |
| Request | pagination, search, categoryId |
| Response | ResponseDto<ProductDto[]> |
| Errors | - |
Product Detail
| Aspect | Details |
|---|---|
| Endpoint | GET /api/products/:slug or /api/mobile/products/:slug |
| Auth | Public |
| Request | - |
| Response | ResponseDto<ProductDto> |
| Errors | 404 not found |
Product Availability
| Aspect | Details |
|---|---|
| Endpoint | GET /api/products/:slug/availability or /api/mobile/products/:slug/availability |
| Auth | Public |
| Request | - |
| Response | ResponseDto<ProductAvailabilityResponseDto> |
| Errors | 404 not found |
Availability payload:
canAddToCart: booleanstockStatus: "in_stock" | "low_stock" | "out_of_stock"availableQuantity: number | nullmaxPurchasableQuantity: number
Computation rules:
status !== publishedorisSellable=false=> out-of-stock (canAddToCart=false)trackInventory=falseorallowOversell=true=> in stock withavailableQuantity=null,maxPurchasableQuantity=99- tracked inventory with
availableQuantity <= 0=> out-of-stock - tracked inventory with
availableQuantity > 0=>in_stockorlow_stockby threshold - missing
product_inventoryrow for physical product => out-of-stock (safe default)
5. Query Parameters
5.1 Admin list (GET /api/admin/products)
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number (1-indexed). |
size | integer | 20 | Items per page (max 100). |
pagination | boolean | true | Set to false to disable pagination. |
search | string | optional | ILIKE search on title, slug, description, category name, and tag name. Also uses pg_trgm similarity scoring on title. |
categoryId | integer | optional | Exact match by category ID. |
parentCategoryId | integer | optional | Match product category or any direct child (2-level hierarchy). |
brandId | integer | optional | Exact match by brand ID. |
brandSeriesId | integer | optional | Exact match by brand series ID. |
productKind | enum | optional | physical, digital, or care_package. |
productTypeId | integer | optional | Exact match by product type ID. |
tagId | integer | optional | Products with the specified tag. |
status | enum | optional | draft, published, unlisted, or archived. |
isSellable | boolean | optional | Filter by sellable flag. |
isBestSeller | boolean | optional | Filter to best-seller flagged products. |
isTrending | boolean | optional | Filter trending products (applies expiry check). |
minPrice | integer | optional | Inclusive minimum selling price (paisa). |
maxPrice | integer | optional | Inclusive maximum selling price (paisa). |
minRating | integer | optional | Minimum average approved rating (1-5). |
sort | enum | updatedAt | Sort field: title, slug, productKind, mrp, sp, status, isSellable, createdAt, updatedAt, relevance, isBestSeller, rating. |
order | enum | desc | Sort direction: asc or desc. |
5.2 Customer list (GET /api/products)
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number (1-indexed). |
size | integer | 20 | Items per page (max 100). |
pagination | boolean | true | Set to false to disable pagination. |
search | string | optional | ILIKE search on title, description, category name, tag name. Uses pg_trgm similarity scoring. |
categoryId | integer | optional | Exact match by category ID. |
parentCategoryId | integer | optional | Match product category or direct child (2-level hierarchy). |
brandId | integer | optional | Exact match by brand ID. |
brandSeriesId | integer | optional | Exact match by brand series ID. |
productKind | enum | optional | physical, digital, or care_package. |
productTypeId | integer | optional | Exact match by product type ID. |
tagId | integer | optional | Products with the specified tag. |
isBestSeller | boolean | optional | Filter to best-seller flagged products. |
isTrending | boolean | optional | Filter trending products (applies expiry check). |
minPrice | integer | optional | Inclusive minimum selling price (paisa). |
maxPrice | integer | optional | Inclusive maximum selling price (paisa). |
minRating | integer | optional | Minimum average approved rating (1-5). |
sort | enum | createdAt | Sort field: title, mrp, sp, createdAt, relevance, isBestSeller, rating. |
order | enum | desc | Sort direction: asc or desc. |
5.3 Featured list (GET /api/products/featured)
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number (1-indexed). |
size | integer | env default | Falls back to CATALOG_FEATURED_DEFAULT_SIZE when omitted. |
pagination | boolean | true | Set to false to disable pagination. |
5.4 Discovery endpoints
| Endpoint | Parameters | Description |
|---|---|---|
GET /api/products/new-arrivals | withinDays (1-365, default 30), categoryId, brandId, page, size | Products created within the specified window, sorted by newest first. |
GET /api/products/popular | categoryId, brandId, page, size | Products with isBestSeller=true and valid expiry. |
GET /api/products/trending | categoryId, brandId, page, size | Products with isTrending=true and valid expiry. |
GET /api/products/best-sellers | categoryId, brandId, page, size | Same semantics as popular (shared implementation). |
All discovery endpoints accept page, size, and pagination from the base QueryDto. No sort/order parameters — results are always sorted by createdAt descending.
6. Response Shape Examples
6.1 Product list item (with average rating)
{
"id": 101,
"title": "Dell XPS 15 Laptop",
"slug": "dell-xps-15-laptop",
"shortSummary": "High-performance laptop with OLED display.",
"thumbnailUrl": "https://cdn.example.com/products/dell-xps-15/thumbnail.jpg",
"productTypeId": 1,
"productType": {
"id": 1,
"name": "Laptop",
"slug": "laptop"
},
"productKind": "physical",
"mrp": 150000,
"sp": 135000,
"discount": 15000,
"isBestSeller": true,
"averageRating": 4.2,
"reviewCount": 12,
"categoryId": 3,
"category": {
"name": "Laptops",
"slug": "laptops"
},
"brand": {
"id": 2,
"name": "Dell",
"slug": "dell",
"imageUrl": "https://cdn.example.com/brands/dell.png"
}
}averageRatingisnullwhen no approved reviews exist.reviewCountis0when no approved reviews exist.categoryis omitted whencategoryIdis null.brandisnullwhen the product has no brand association.
6.2 Product detail response example
{
"id": 101,
"title": "Hand-Painted Green Tara Thangka",
"slug": "hand-painted-green-tara-thangka",
"shortSummary": "Traditional Green Tara thangka painted by Kathmandu artisans.",
"description": "Authentic Green Tara thangka created using natural pigments on cotton canvas.",
"productKind": "physical",
"productTypeId": 1,
"productType": {
"id": 1,
"name": "Thangka",
"slug": "thangka"
},
"isBestSeller": true,
"specifications": {
"material": "Cotton canvas",
"pigment": "Natural mineral"
},
"mrp": 15000,
"sp": 12000,
"discount": 3000,
"currency": "USD",
"totalReviews": 12,
"averageRating": 4.5,
"reviewStatus": "Reviews available",
"thumbnailUrl": "https://cdn.example.com/products/thangka/thumbnail.jpg",
"galleryUrls": [
"https://cdn.example.com/products/thangka/gallery-1.jpg",
"https://cdn.example.com/products/thangka/gallery-2.jpg"
],
"isSellable": true,
"categoryId": 3,
"category": {
"id": 3,
"name": "Thangka Paintings",
"slug": "thangka-paintings"
},
"brand": null,
"tags": [
{ "id": 7, "name": "Handcrafted in Nepal", "slug": "handcrafted-in-nepal" }
],
"createdAt": "2026-02-21T10:00:00.000Z"
}7. Enums
productKind:physical | digital | care_packageproductStatus(admin flows):draft | published | unlisted | archived- Sort enums follow DTO-level allowlists (admin/customer differ).
8. Integration Diagram
9. Caching
| Prefix | Usage |
|---|---|
catalog:products:list: | customer list cache |
catalog:product:slug: | detail-by-slug cache |
catalog:products:featured: | customer featured list cache |
catalog:products:new-arrivals: | new arrivals discovery cache |
catalog:products:popular: | popular products discovery cache |
catalog:products:trending: | trending products discovery cache |
catalog:products:best-sellers: | best sellers discovery cache |
GET /products/:slug/availability is intentionally uncached so stock actions (Add to Cart, Purchase Now) use live inventory status.
Write invalidation happens on admin create/update/delete via prefix invalidation. All discovery caches (new-arrivals, popular, trending, best-sellers) are invalidated on the same mutation events as the main product list cache.
10. Error Handling
| HTTP | errorCode | Condition |
|---|---|---|
| 400 | PRODUCT_SP_EXCEEDS_MRP | Selling price greater than MRP |
| 400 | PRODUCT_INVALID_STATUS_TRANSITION | Invalid status transition |
| 400 | PRODUCT_CREATE_FAILED | Create returned no row |
| 400 | PRODUCT_INVALID_SORT | Unsupported sort field |
| 400 | PRODUCT_INVALID_WITHIN_DAYS | withinDays parameter out of range (1-365) |
| 400 | PRODUCT_INVALID_RATING | minRating parameter out of range (1-5) |
| 404 | PRODUCT_NOT_FOUND | Product missing |
| 404 | PRODUCT_CATEGORY_NOT_FOUND | Category ID missing |
| 404 | PRODUCT_TAG_NOT_FOUND | One or more tags missing |
| 404 | PRODUCT_BRAND_NOT_FOUND | Brand not found |
| 404 | PRODUCT_SERIES_NOT_FOUND | Brand series not found |
| 429 | RATE_LIMIT_EXCEEDED | Search/list limiter exceeded |
11. Endpoint Reference + Payload Cheatsheet
11.1 Payload Cheatsheet Table (Every Endpoint)
| Method | Path | Auth / Permission | Request DTO / Params | Success DTO | Notes |
|---|---|---|---|---|---|
| GET | /api/admin/products | Admin + Products_READ | QueryProductsAdminDto "" page?, size?, pagination?, search?, categoryId?, productTypeId?, tagId?, status?, isSellable?, sort?, order? "" | ProductAdminListItemDto[] paginated | Admin product list with filters |
| GET | /api/admin/products/:id | Admin + Products_READ | path: id (int) | ProductAdminDetailDto | Admin product detail with history |
| POST | /api/admin/products | Admin + Products_CREATE | body: CreateProductDto "" title, slug, description?, shortSummary?, productKind, productTypeId, isBestSeller, specifications?, mrp, sp, categoryId, tagIds?, thumbnailUrl?, galleryUrls?, metadata?, mainFileUrl?, previewFileUrl?, attachments?, isSellable?, status? "" | ProductAdminDetailDto | Creates new product |
| PATCH | /api/admin/products/:id | Admin + Products_UPDATE | path: id (int), body: UpdateProductDto "" title?, slug?, description?, shortSummary?, productKind?, productTypeId?, isBestSeller?, specifications?, mrp?, sp?, categoryId?, tagIds?, thumbnailUrl?, galleryUrls?, metadata?, mainFileUrl?, previewFileUrl?, attachments?, isSellable?, status? "" | ProductAdminDetailDto | Updates product |
| DELETE | /api/admin/products/:id | Admin + Products_DELETE | path: id (int) | "" success: true "" | Soft deletes product |
| GET | /api/products | Public | QueryProductsDto "" page?, size?, pagination?, search?, categoryId?, parentCategoryId?, brandId?, brandSeriesId?, productKind?, productTypeId?, tagId?, isBestSeller?, isTrending?, minPrice?, maxPrice?, minRating?, sort?, order? "" | ProductListItemDto[] paginated | Customer product list with full filter/sort |
| GET | /api/mobile/products | Public | QueryProductsDto (same as customer) | ProductListItemDto[] paginated | Mobile product list (same as customer) |
| GET | /api/products/featured | Public | QueryFeaturedProductsDto "" page?, size?, pagination? "" | ProductListItemDto[] paginated | Featured products list |
| GET | /api/mobile/products/featured | Public | QueryFeaturedProductsDto "" page?, size?, pagination? "" | ProductListItemDto[] paginated | Mobile featured products |
| GET | /api/products/new-arrivals | Public | FetchNewArrivalsDto "" withinDays?, categoryId?, brandId?, page?, size? "" | ProductListItemDto[] paginated | Products created within N days, sorted by newest |
| GET | /api/products/popular | Public | FetchDiscoveryDto "" categoryId?, brandId?, page?, size? "" | ProductListItemDto[] paginated | Best-seller products with valid expiry |
| GET | /api/products/trending | Public | FetchDiscoveryDto "" categoryId?, brandId?, page?, size? "" | ProductListItemDto[] paginated | Trending products with valid expiry |
| GET | /api/products/best-sellers | Public | FetchDiscoveryDto (same as popular) | ProductListItemDto[] paginated | Same semantics as popular, separate cache |
| GET | /api/products/:slug | Public | path: slug (string) | ProductDetailDto | Product detail by slug (supports historical slugs) |
| GET | /api/mobile/products/:slug | Public | path: slug (string) | ProductDetailDto | Mobile product detail by slug |
11.1.1 Admin List Endpoint Query Variations
| Method | Path | Query Parameters | Notes |
|---|---|---|---|
| GET | /api/admin/products | (no params) | Default: page=1, size=20, sort=createdAt, order=desc |
| GET | /api/admin/products | ?page=2&size=10 | Custom page and size |
| GET | /api/admin/products | ?categoryId=3 | Filter by category |
| GET | /api/admin/products | ?status=published | Filter by status |
| GET | /api/admin/products | ?isSellable=true | Filter sellable products |
| GET | /api/admin/products | ?tagId=7 | Filter by tag |
| GET | /api/admin/products | ?search=tara | Search by title/slug/description |
| GET | /api/admin/products | ?sort=title&order=asc | Sort by title ascending |
| GET | /api/admin/products | ?sort=mrp&order=desc | Sort by MRP descending |
| GET | /api/admin/products | ?status=published&isSellable=true | Combined filters |
11.1.2 Customer/Mobile List Endpoint Query Variations
| Method | Path | Query Parameters | Notes |
|---|---|---|---|
| GET | /api/products | (no params) | Default: page=1, size=20, sort=relevance, order=desc |
| GET | /api/products | ?page=2&size=10 | Custom pagination |
| GET | /api/products | ?categoryId=3 | Filter by category |
| GET | /api/products | ?tagId=7 | Filter by tag |
| GET | /api/products | ?sort=title&order=asc | Sort by title |
| GET | /api/products | ?sort=mrp&order=desc | Sort by MRP descending |
| GET | /api/products | ?sort=sp&order=asc | Sort by selling price ascending |
| GET | /api/products | ?sort=createdAt&order=desc | Sort by creation date |
| GET | /api/products | ?pagination=false | Return all matching records |
| GET | /api/products | ?brandId=2 | Filter by brand |
| GET | /api/products | ?brandSeriesId=5 | Filter by brand series |
| GET | /api/products | ?isBestSeller=true | Filter best-seller products |
| GET | /api/products | ?isTrending=true | Filter trending products |
| GET | /api/products | ?minPrice=1000&maxPrice=5000 | Filter by selling price range |
| GET | /api/products | ?minRating=4 | Filter by minimum average rating |
| GET | /api/products | ?sort=rating&order=desc | Sort by average rating descending |
| GET | /api/products | ?sort=isBestSeller&order=desc | Sort by best-seller flag |
| GET | /api/products | ?parentCategoryId=3 | Products in category 3 and its children |
| GET | /api/products | ?brandId=2&minPrice=5000&minRating=3 | Combined brand, price, and rating filters |
11.1.3 Featured Products Query Variations
| Method | Path | Query Parameters | Notes |
|---|---|---|---|
| GET | /api/products/featured | (no params) | Uses default size from env |
| GET | /api/products/featured | ?page=1&size=10 | Custom page/size |
| GET | /api/products/featured | ?pagination=false | All featured products |
| GET | /api/mobile/products/featured | ?size=20 | Mobile featured with size |
11.1.3.1 Discovery Endpoints Query Variations
| Method | Path | Query Parameters | Notes |
|---|---|---|---|
| GET | /api/products/new-arrivals | (no params) | Default: withinDays=30, page=1, size=20 |
| GET | /api/products/new-arrivals | ?withinDays=7 | Last 7 days |
| GET | /api/products/new-arrivals | ?categoryId=3 | Filter by category |
| GET | /api/products/new-arrivals | ?brandId=2 | Filter by brand |
| GET | /api/products/new-arrivals | ?withinDays=90&categoryId=3&brandId=2 | Combined filters |
| GET | /api/products/popular | (no params) | All popular products |
| GET | /api/products/popular | ?categoryId=3 | Popular in category |
| GET | /api/products/popular | ?brandId=2 | Popular by brand |
| GET | /api/products/trending | (no params) | All trending products |
| GET | /api/products/trending | ?categoryId=3 | Trending in category |
| GET | /api/products/best-sellers | (no params) | Same as popular, separate cache |
11.1.4 Create Product Request Variations
| Method | Path | Request Body | Notes |
|---|---|---|---|
| POST | /api/admin/products | { "title": "Dell XPS 15 Laptop", "slug": "dell-xps-15-laptop", "productKind": "physical", "productTypeId": 1, "mrp": 189900, "sp": 174900, "categoryId": 3 } | Minimal physical product |
| POST | /api/admin/products | { "title": "MS Office 365 Home", "slug": "ms-office-365-home", "productKind": "digital", "productTypeId": 3, "mrp": 6999, "sp": 5999, "categoryId": 5 } | Digital product |
| POST | /api/admin/products | { "title": "2-Year Extended Care", "slug": "2yr-extended-care", "productKind": "care_package", "productTypeId": 4, "mrp": 8999, "sp": 7999, "categoryId": 6, "specifications": { "coverageYears": 2 } } | Care package with specs |
11.1.5 Update Product Request Variations
| Method | Path | Request Body | Notes |
|---|---|---|---|
| PATCH | /api/admin/products/101 | "" "sp": 9000 "" | Update only selling price |
| PATCH | /api/admin/products/101 | "" "status": "published" "" | Publish product |
| PATCH | /api/admin/products/101 | "" "isSellable": false "" | Mark as not sellable |
| PATCH | /api/admin/products/101 | "" "title": "New Title", "slug": "new-slug" "" | Update title and slug |
| PATCH | /api/admin/products/101 | "" "tags": [1,2,3] "" | Update tags |
| PATCH | /api/admin/products/101 | "" "status": "archived" "" | Archive product |
11.1.6 Product Status Flow States
| Status | IsSellable | Next Possible States | Notes |
|---|---|---|---|
draft | false | published, unlisted | Initial state |
published | true | unlisted, archived | Live on storefront |
unlisted | false | published, archived | Hidden but accessible via direct link |
archived | false | - (terminal) | No longer accessible |
11.1.7 Product Kind Availability Behavior
| productKind | Availability semantics |
|---|---|
physical | Stock-tracked via product_inventory. availableQuantity reflects live inventory. |
digital | Always available. canAddToCart: true, availableQuantity: null, maxPurchasableQuantity: 99. |
care_package | Always available. Same semantics as digital. |
11.1.8 Error Response Variations
| HTTP | errorCode | Message | Scenario |
|---|---|---|---|
| 400 | PRODUCT_SP_EXCEEDS_MRP | Selling price cannot exceed MRP. | SP > MRP on create/update |
| 400 | PRODUCT_INVALID_STATUS_TRANSITION | Invalid status transition. | Invalid status change |
| 400 | PRODUCT_CREATE_FAILED | Failed to create product. | Insert returned no row |
| 400 | PRODUCT_INVALID_SORT | Invalid sort field. | Unsupported sort value |
| 400 | PRODUCT_MISSING_REQUIRED_FIELD | Required field is missing. | title, productKind, productTypeId, mrp, sp, categoryId |
| 404 | PRODUCT_NOT_FOUND | Product not found. | Invalid product ID |
| 404 | PRODUCT_CATEGORY_NOT_FOUND | Category not found. | Invalid categoryId |
| 404 | PRODUCT_TAG_NOT_FOUND | One or more tags not found. | Invalid tag IDs |
| 409 | PRODUCT_SLUG_EXISTS | Product with this slug already exists. | Duplicate slug |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests. | Rate limit exceeded |
11.1.9 Cache Key Patterns
| Cache Key | Pattern | TTL (seconds) | Invalidation |
|---|---|---|---|
| Customer product list | catalog:products:list:pagination:"page"-size:"size"-sort:"sort"-order:"order"-filters... | 300 | Admin create/update/delete |
| Product detail by slug | catalog:product:slug:"slug" | 600 | Admin update/delete |
| Featured products list | catalog:products:featured:pagination:"page"-size:"size" | 300 | Admin create/update/delete featured |
| Admin product list | catalog:products:admin:list:pagination:"page"-size:"size"-filters... | 60 | Admin mutations |
| New arrivals | catalog:products:new-arrivals:withinDays-"-categoryId-"-brandId-"..."` | 300 | Admin create/update/delete |
| Popular products | catalog:products:popular:categoryId-"-brandId-"..." | 300 | Admin create/update/delete |
| Trending products | catalog:products:trending:categoryId-"-brandId-"..." | 300 | Admin create/update/delete |
| Best sellers | catalog:products:best-sellers:categoryId-"-brandId-"..." | 300 | Admin create/update/delete |
11.1.10 Rate Limit Patterns
| Route Family | Key Pattern | Limit |
|---|---|---|
| Customer list | rl:catalog:products:customer:list-ip:"ip" | 60 req/min |
| Customer detail | rl:catalog:products:customer:detail-ip:"ip" | 120 req/min |
| Featured list | rl:catalog:products:featured-ip:"ip" | 60 req/min |
| Discovery endpoints | rl:catalog:products:discovery-ip:"ip" | 60 req/min |
| Admin list | rl:catalog:products:admin:list-key:"adminId" | 60 req/min |
| Admin mutations | rl:catalog:products:admin:mutate-key:"adminId" | 30 req/min |
11.1.11 DTO Field Reference
CreateProductDto
| Field | Type | Required | Validation | Notes |
|---|---|---|---|---|---|
| title | string | Yes | 1-200 chars | Product title |
| slug | string | Yes | 1-100 chars, URL-safe | URL slug |
| description | string | No | Max 5000 chars | Full description |
| shortSummary | string | No | Max 500 chars | Brief summary |
| productKind | enum | Yes | physical | digital | care_package | Determines availability behavior and fulfillment type |
| productTypeId | integer | Yes | Positive | FK to product_type.id |
| brandId | integer | No | Positive | Brand reference — nullable FK to brand.id |
| specifications | object | No | JSON object | Flexible per-product-type specs (e.g. processor, ram, coverage) |
| isBestSeller | boolean | Yes | boolean | Bestseller product flag |
| mrp | number | Yes | Positive integer | Maximum retail price (integer minor units) |
| sp | number | Yes | Positive integer, not more than mrp | Selling price (integer minor units) |
| categoryId | integer | Yes | Positive | Category reference |
| tagIds | number[] | No | Valid unique positive tag IDs | Tag references |
| thumbnailUrl | string | No | Valid URL | Thumbnail image |
| galleryUrls | string[] | No | Valid URLs | Product gallery images |
| metadata | object | No | JSON object | Product metadata |
| mainFileUrl | string | No | Valid URL | Main downloadable/media file URL |
| previewFileUrl | string | No | Valid URL | Preview file URL |
| attachments | object[] | No | name/url/type fields | Additional asset attachments |
| isSellable | boolean | No | boolean | Sellable visibility |
| status | enum | No | draft|published|unlisted|archived | Initial status |
UpdateProductDto
| Field | Type | Required | Validation | Notes |
|---|---|---|---|---|
| title | string | No | 1-200 chars | Product title |
| slug | string | No | 1-100 chars, URL-safe | URL slug |
| description | string | No | Max 5000 chars | Full description |
| shortSummary | string | No | Max 500 chars | Brief summary |
| productKind | enum | No | physical | digital | care_package | Determines availability behavior and fulfillment type |
| productTypeId | integer | No | Positive | FK to product_type.id |
| specifications | object | No | JSON object | Flexible per-product-type specs (e.g. processor, ram, coverage) |
| isBestSeller | boolean | No | boolean | Bestseller product flag |
| mrp | number | No | Positive integer | Maximum retail price |
| sp | number | No | Positive integer, not more than mrp | Selling price |
| categoryId | integer | No | Positive | Category reference |
| tagIds | number[] | No | Valid unique positive tag IDs | Tag references |
| thumbnailUrl | string | No | Valid URL | Thumbnail image |
| galleryUrls | string[] | No | Valid URLs | Product gallery images |
| metadata | object | No | JSON object | Product metadata |
| mainFileUrl | string | No | Valid URL | Main downloadable/media file URL |
| previewFileUrl | string | No | Valid URL | Preview file URL |
| attachments | object[] | No | name/url/type fields | Additional asset attachments |
| status | enum | No | draft|published|unlisted|archived | Product status |
| isSellable | boolean | No | - | Sellable flag |
QueryProductsDto / QueryProductsAdminDto
| Field | Type | Required | Notes |
|---|---|---|---|
| page | number | No | Page number (default 1) |
| size | number | No | Page size (default 20, max 100) |
| pagination | boolean | No | Enable pagination (default true) |
| search | string | No | Search query |
| categoryId | number | No | Filter by category |
| productKind | enum | No | Filter by physical | digital | care_package |
| productTypeId | number | No | Filter by product type ID |
| tagId | number | No | Filter by tag |
| brandId | number | No | Filter by brand ID |
| brandSeriesId | number | No | Filter by brand series ID |
| isBestSeller | boolean | No | Filter best-seller products |
| isTrending | boolean | No | Filter trending products |
| minPrice | number | No | Minimum selling price (inclusive) |
| maxPrice | number | No | Maximum selling price (inclusive) |
| minRating | number | No | Minimum average rating (1-5) |
| sort | string | No | Sort field |
| order | string | No | Sort order (asc/desc) |
| status | enum | No | (admin only) Filter by status |
| isSellable | boolean | No | (admin only) Filter sellable |
ProductListItemDto
| Field | Type | Notes |
|---|---|---|
| id | number | Product ID |
| title | string | Product title |
| slug | string | URL slug |
| shortSummary | string | Brief summary |
| thumbnailUrl | string | Thumbnail image URL |
| productTypeId | number | FK to product_type.id |
| productType | object | { id, name, slug } product type summary |
| productKind | enum | physical | digital | care_package |
| mrp | number | Maximum retail price (paisa) |
| sp | number | Selling price (paisa) |
| discount | number | MRP - SP |
| isBestSeller | boolean | Bestseller flag |
| averageRating | number | Average approved rating (null when none) |
| reviewCount | number | Count of approved reviews (0 when none) |
| categoryId | number | Category reference |
| category | object | { name, slug } — omitted when categoryId is null |
| brand | object | { id, name, slug, imageUrl } — null when no brand |
ProductResponseDto
| Field | Type | Notes |
|---|---|---|
| id | number | Product ID |
| title | string | Product title |
| slug | string | URL slug |
| shortSummary | string | Brief summary |
| description | string | Full description |
| productKind | enum | physical | digital | care_package |
| productTypeId | number | FK to product_type.id |
| productType | object | { id, name, slug } product type summary |
| isBestSeller | boolean | Bestseller flag |
| specifications | object | Flexible per-product-type specs |
| mrp | number | Maximum retail price (paisa) |
| sp | number | Selling price (paisa) |
| discount | number | MRP - SP |
| currency | string | Currency code (e.g. USD) |
| totalReviews | number | Total approved review count |
| averageRating | number | Average approved rating |
| reviewStatus | string | "Reviews available" or "No reviews yet" |
| thumbnailUrl | string | Thumbnail image URL |
| galleryUrls | string[] | Product gallery images |
| isSellable | boolean | Sellable visibility |
| categoryId | number | Category reference |
| category | object | { id, name, slug } |
| brand | object | BrandSummaryDto { id, name, slug, imageUrl? } — null when no brand |
| tags | array | ProductTagSummaryDto[] |
| createdAt | string | ISO 8601 timestamp |
| updatedAt | string | ISO 8601 timestamp |
FetchNewArrivalsDto
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
| withinDays | number | No | 30 | 1-365. Window for product creation date. |
| categoryId | number | No | - | Filter by category. |
| brandId | number | No | - | Filter by brand. |
| page | number | No | 1 | Page number. |
| size | number | No | 20 | Page size (max 100). |
FetchDiscoveryDto (shared by popular/trending)
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
| categoryId | number | No | - | Filter by category. |
| brandId | number | No | - | Filter by brand. |
| page | number | No | 1 | Page number. |
| size | number | No | 20 | Page size (max 100). |
12. Testing Scenarios
12.1 Suggested Test Scenarios
- Admin creates product with all fields, publishes it, and product appears in customer list.
- Product slug changed in admin; old slug still resolves to canonical detail via historical slug fallback.
- Customer list only returns
isSellable=trueandstatus=publishedproducts; draft products hidden. - Customer with search query "tara" returns matching products by title, shortSummary, or description.
- Featured products endpoint returns only active slots with in-window date ranges.
429rate limit exceeded on customer list/search with sliding window Redis keys.- New arrivals endpoint with
withinDays=7returns only products created within the last 7 days, sorted newest first. - Popular endpoint excludes products with expired
isBestSellerExpiresAt, even whenisBestSeller=true. - Trending endpoint excludes products with expired
trendingExpiresAt, even whenisTrending=true. - Brand filter (
?brandId=2) correctly scopes discovery endpoint results. - Filter by
minRating=4on customer list returns only products with average approved rating >= 4. - Sort by
ratingon customer list returns products ordered by descending average rating. PRODUCT_BRAND_NOT_FOUNDreturned for invalid brand ID in discovery filters.PRODUCT_INVALID_WITHIN_DAYSreturned forwithinDaysvalue outside 1-365 range.
13. Discovery Endpoints — Detail
13.1 New Arrivals
- Endpoint:
GET /api/products/new-arrivals - Auth: Public
- Purpose: Surface recently created products, ideal for "New In" or "Just Arrived" sections.
- Core logic:
createdAt >= NOW() - withinDays INTERVAL, sorted bycreatedAt DESC. - Parameters:
withinDays(1-365, default 30),categoryId,brandId,page,size. - DTO:
FetchNewArrivalsDto - Cache prefix:
catalog:products:new-arrivals:
Cache key incorporates withinDays, categoryId, brandId, page, and size.
13.2 Popular
- Endpoint:
GET /api/products/popular - Auth: Public
- Purpose: Surface products flagged as best sellers, ideal for "Popular" or "Best Sellers" sections.
- Core logic:
isBestSeller = true AND (isBestSellerExpiresAt IS NULL OR isBestSellerExpiresAt > NOW()), sorted bycreatedAt DESC. - Parameters:
categoryId,brandId,page,size. - DTO:
FetchDiscoveryDto - Cache prefix:
catalog:products:popular:
13.3 Trending
- Endpoint:
GET /api/products/trending - Auth: Public
- Purpose: Surface products flagged as trending, ideal for "Trending Now" sections.
- Core logic:
isTrending = true AND (trendingExpiresAt IS NULL OR trendingExpiresAt > NOW()), sorted bycreatedAt DESC. - Parameters:
categoryId,brandId,page,size. - DTO:
FetchDiscoveryDto - Cache prefix:
catalog:products:trending:
13.4 Best Sellers
- Endpoint:
GET /api/products/best-sellers - Auth: Public
- Purpose: Same semantics as Popular — returns
isBestSeller=trueproducts with valid expiry. - Difference: Separate cache prefix (
catalog:products:best-sellers:) allows independent cache management.
13.5 Brand-Series Detail
- Endpoint:
GET /api/brand-series/:slug - Auth: Public
- Purpose: Returns brand series detail with nested product listing for that series.
- Resolution: Series slug resolved through
brand_series_slug_historyfor historical slug support.
See Also
- Feature Guide: See Catalog Product - Feature List Section 17 (Release/QA Checklist) for product lifecycle and Section 13-16 for filter, sort, rating, brand discovery, and new arrivals/popular/trending feature descriptions.
- Backend Reference: See Catalog Product - Backend Documentation Section 9 for system architecture diagram.
- Brand Module: See Catalog Brand - Feature List for brand list/detail and slug history capabilities.
- Brand Series Module: See Brand Series - Feature List for series discovery endpoints.