Shop It Docs
Developer ResourcescatalogProduct

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

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=true and status=published.
  • Canonical slug: current slug returned in payload.
  • Historical slug: old slug stored in product_slug_history and 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

MethodPathPermission
GET/api/admin/productsProducts_READ
GET/api/admin/products/:idProducts_READ
POST/api/admin/productsProducts_CREATE
PATCH/api/admin/products/:idProducts_UPDATE
DELETE/api/admin/products/:idProducts_DELETE

Customer + Mobile

MethodPath
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

AspectDetails
EndpointGET /api/products or /api/mobile/products
AuthPublic
Requestpagination, search, categoryId
ResponseResponseDto<ProductDto[]>
Errors-

Product Detail

AspectDetails
EndpointGET /api/products/:slug or /api/mobile/products/:slug
AuthPublic
Request-
ResponseResponseDto<ProductDto>
Errors404 not found

Product Availability

AspectDetails
EndpointGET /api/products/:slug/availability or /api/mobile/products/:slug/availability
AuthPublic
Request-
ResponseResponseDto<ProductAvailabilityResponseDto>
Errors404 not found

Availability payload:

  • canAddToCart: boolean
  • stockStatus: "in_stock" | "low_stock" | "out_of_stock"
  • availableQuantity: number | null
  • maxPurchasableQuantity: number

Computation rules:

  • status !== published or isSellable=false => out-of-stock (canAddToCart=false)
  • trackInventory=false or allowOversell=true => in stock with availableQuantity=null, maxPurchasableQuantity=99
  • tracked inventory with availableQuantity <= 0 => out-of-stock
  • tracked inventory with availableQuantity > 0 => in_stock or low_stock by threshold
  • missing product_inventory row for physical product => out-of-stock (safe default)

5. Query Parameters

5.1 Admin list (GET /api/admin/products)

ParameterTypeDefaultDescription
pageinteger1Page number (1-indexed).
sizeinteger20Items per page (max 100).
paginationbooleantrueSet to false to disable pagination.
searchstringoptionalILIKE search on title, slug, description, category name, and tag name. Also uses pg_trgm similarity scoring on title.
categoryIdintegeroptionalExact match by category ID.
parentCategoryIdintegeroptionalMatch product category or any direct child (2-level hierarchy).
brandIdintegeroptionalExact match by brand ID.
brandSeriesIdintegeroptionalExact match by brand series ID.
productKindenumoptionalphysical, digital, or care_package.
productTypeIdintegeroptionalExact match by product type ID.
tagIdintegeroptionalProducts with the specified tag.
statusenumoptionaldraft, published, unlisted, or archived.
isSellablebooleanoptionalFilter by sellable flag.
isBestSellerbooleanoptionalFilter to best-seller flagged products.
isTrendingbooleanoptionalFilter trending products (applies expiry check).
minPriceintegeroptionalInclusive minimum selling price (paisa).
maxPriceintegeroptionalInclusive maximum selling price (paisa).
minRatingintegeroptionalMinimum average approved rating (1-5).
sortenumupdatedAtSort field: title, slug, productKind, mrp, sp, status, isSellable, createdAt, updatedAt, relevance, isBestSeller, rating.
orderenumdescSort direction: asc or desc.

5.2 Customer list (GET /api/products)

ParameterTypeDefaultDescription
pageinteger1Page number (1-indexed).
sizeinteger20Items per page (max 100).
paginationbooleantrueSet to false to disable pagination.
searchstringoptionalILIKE search on title, description, category name, tag name. Uses pg_trgm similarity scoring.
categoryIdintegeroptionalExact match by category ID.
parentCategoryIdintegeroptionalMatch product category or direct child (2-level hierarchy).
brandIdintegeroptionalExact match by brand ID.
brandSeriesIdintegeroptionalExact match by brand series ID.
productKindenumoptionalphysical, digital, or care_package.
productTypeIdintegeroptionalExact match by product type ID.
tagIdintegeroptionalProducts with the specified tag.
isBestSellerbooleanoptionalFilter to best-seller flagged products.
isTrendingbooleanoptionalFilter trending products (applies expiry check).
minPriceintegeroptionalInclusive minimum selling price (paisa).
maxPriceintegeroptionalInclusive maximum selling price (paisa).
minRatingintegeroptionalMinimum average approved rating (1-5).
sortenumcreatedAtSort field: title, mrp, sp, createdAt, relevance, isBestSeller, rating.
orderenumdescSort direction: asc or desc.
ParameterTypeDefaultDescription
pageinteger1Page number (1-indexed).
sizeintegerenv defaultFalls back to CATALOG_FEATURED_DEFAULT_SIZE when omitted.
paginationbooleantrueSet to false to disable pagination.

5.4 Discovery endpoints

EndpointParametersDescription
GET /api/products/new-arrivalswithinDays (1-365, default 30), categoryId, brandId, page, sizeProducts created within the specified window, sorted by newest first.
GET /api/products/popularcategoryId, brandId, page, sizeProducts with isBestSeller=true and valid expiry.
GET /api/products/trendingcategoryId, brandId, page, sizeProducts with isTrending=true and valid expiry.
GET /api/products/best-sellerscategoryId, brandId, page, sizeSame 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"
  }
}
  • averageRating is null when no approved reviews exist.
  • reviewCount is 0 when no approved reviews exist.
  • category is omitted when categoryId is null.
  • brand is null when 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_package
  • productStatus (admin flows): draft | published | unlisted | archived
  • Sort enums follow DTO-level allowlists (admin/customer differ).

8. Integration Diagram

9. Caching

PrefixUsage
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

HTTPerrorCodeCondition
400PRODUCT_SP_EXCEEDS_MRPSelling price greater than MRP
400PRODUCT_INVALID_STATUS_TRANSITIONInvalid status transition
400PRODUCT_CREATE_FAILEDCreate returned no row
400PRODUCT_INVALID_SORTUnsupported sort field
400PRODUCT_INVALID_WITHIN_DAYSwithinDays parameter out of range (1-365)
400PRODUCT_INVALID_RATINGminRating parameter out of range (1-5)
404PRODUCT_NOT_FOUNDProduct missing
404PRODUCT_CATEGORY_NOT_FOUNDCategory ID missing
404PRODUCT_TAG_NOT_FOUNDOne or more tags missing
404PRODUCT_BRAND_NOT_FOUNDBrand not found
404PRODUCT_SERIES_NOT_FOUNDBrand series not found
429RATE_LIMIT_EXCEEDEDSearch/list limiter exceeded

11. Endpoint Reference + Payload Cheatsheet

11.1 Payload Cheatsheet Table (Every Endpoint)

MethodPathAuth / PermissionRequest DTO / ParamsSuccess DTONotes
GET/api/admin/productsAdmin + Products_READQueryProductsAdminDto "" page?, size?, pagination?, search?, categoryId?, productTypeId?, tagId?, status?, isSellable?, sort?, order? ""ProductAdminListItemDto[] paginatedAdmin product list with filters
GET/api/admin/products/:idAdmin + Products_READpath: id (int)ProductAdminDetailDtoAdmin product detail with history
POST/api/admin/productsAdmin + Products_CREATEbody: CreateProductDto "" title, slug, description?, shortSummary?, productKind, productTypeId, isBestSeller, specifications?, mrp, sp, categoryId, tagIds?, thumbnailUrl?, galleryUrls?, metadata?, mainFileUrl?, previewFileUrl?, attachments?, isSellable?, status? ""ProductAdminDetailDtoCreates new product
PATCH/api/admin/products/:idAdmin + Products_UPDATEpath: id (int), body: UpdateProductDto "" title?, slug?, description?, shortSummary?, productKind?, productTypeId?, isBestSeller?, specifications?, mrp?, sp?, categoryId?, tagIds?, thumbnailUrl?, galleryUrls?, metadata?, mainFileUrl?, previewFileUrl?, attachments?, isSellable?, status? ""ProductAdminDetailDtoUpdates product
DELETE/api/admin/products/:idAdmin + Products_DELETEpath: id (int)"" success: true ""Soft deletes product
GET/api/productsPublicQueryProductsDto "" page?, size?, pagination?, search?, categoryId?, parentCategoryId?, brandId?, brandSeriesId?, productKind?, productTypeId?, tagId?, isBestSeller?, isTrending?, minPrice?, maxPrice?, minRating?, sort?, order? ""ProductListItemDto[] paginatedCustomer product list with full filter/sort
GET/api/mobile/productsPublicQueryProductsDto (same as customer)ProductListItemDto[] paginatedMobile product list (same as customer)
GET/api/products/featuredPublicQueryFeaturedProductsDto "" page?, size?, pagination? ""ProductListItemDto[] paginatedFeatured products list
GET/api/mobile/products/featuredPublicQueryFeaturedProductsDto "" page?, size?, pagination? ""ProductListItemDto[] paginatedMobile featured products
GET/api/products/new-arrivalsPublicFetchNewArrivalsDto "" withinDays?, categoryId?, brandId?, page?, size? ""ProductListItemDto[] paginatedProducts created within N days, sorted by newest
GET/api/products/popularPublicFetchDiscoveryDto "" categoryId?, brandId?, page?, size? ""ProductListItemDto[] paginatedBest-seller products with valid expiry
GET/api/products/trendingPublicFetchDiscoveryDto "" categoryId?, brandId?, page?, size? ""ProductListItemDto[] paginatedTrending products with valid expiry
GET/api/products/best-sellersPublicFetchDiscoveryDto (same as popular)ProductListItemDto[] paginatedSame semantics as popular, separate cache
GET/api/products/:slugPublicpath: slug (string)ProductDetailDtoProduct detail by slug (supports historical slugs)
GET/api/mobile/products/:slugPublicpath: slug (string)ProductDetailDtoMobile product detail by slug

11.1.1 Admin List Endpoint Query Variations

MethodPathQuery ParametersNotes
GET/api/admin/products(no params)Default: page=1, size=20, sort=createdAt, order=desc
GET/api/admin/products?page=2&size=10Custom page and size
GET/api/admin/products?categoryId=3Filter by category
GET/api/admin/products?status=publishedFilter by status
GET/api/admin/products?isSellable=trueFilter sellable products
GET/api/admin/products?tagId=7Filter by tag
GET/api/admin/products?search=taraSearch by title/slug/description
GET/api/admin/products?sort=title&order=ascSort by title ascending
GET/api/admin/products?sort=mrp&order=descSort by MRP descending
GET/api/admin/products?status=published&isSellable=trueCombined filters

11.1.2 Customer/Mobile List Endpoint Query Variations

MethodPathQuery ParametersNotes
GET/api/products(no params)Default: page=1, size=20, sort=relevance, order=desc
GET/api/products?page=2&size=10Custom pagination
GET/api/products?categoryId=3Filter by category
GET/api/products?tagId=7Filter by tag
GET/api/products?sort=title&order=ascSort by title
GET/api/products?sort=mrp&order=descSort by MRP descending
GET/api/products?sort=sp&order=ascSort by selling price ascending
GET/api/products?sort=createdAt&order=descSort by creation date
GET/api/products?pagination=falseReturn all matching records
GET/api/products?brandId=2Filter by brand
GET/api/products?brandSeriesId=5Filter by brand series
GET/api/products?isBestSeller=trueFilter best-seller products
GET/api/products?isTrending=trueFilter trending products
GET/api/products?minPrice=1000&maxPrice=5000Filter by selling price range
GET/api/products?minRating=4Filter by minimum average rating
GET/api/products?sort=rating&order=descSort by average rating descending
GET/api/products?sort=isBestSeller&order=descSort by best-seller flag
GET/api/products?parentCategoryId=3Products in category 3 and its children
GET/api/products?brandId=2&minPrice=5000&minRating=3Combined brand, price, and rating filters
MethodPathQuery ParametersNotes
GET/api/products/featured(no params)Uses default size from env
GET/api/products/featured?page=1&size=10Custom page/size
GET/api/products/featured?pagination=falseAll featured products
GET/api/mobile/products/featured?size=20Mobile featured with size

11.1.3.1 Discovery Endpoints Query Variations

MethodPathQuery ParametersNotes
GET/api/products/new-arrivals(no params)Default: withinDays=30, page=1, size=20
GET/api/products/new-arrivals?withinDays=7Last 7 days
GET/api/products/new-arrivals?categoryId=3Filter by category
GET/api/products/new-arrivals?brandId=2Filter by brand
GET/api/products/new-arrivals?withinDays=90&categoryId=3&brandId=2Combined filters
GET/api/products/popular(no params)All popular products
GET/api/products/popular?categoryId=3Popular in category
GET/api/products/popular?brandId=2Popular by brand
GET/api/products/trending(no params)All trending products
GET/api/products/trending?categoryId=3Trending in category
GET/api/products/best-sellers(no params)Same as popular, separate cache

11.1.4 Create Product Request Variations

MethodPathRequest BodyNotes
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

MethodPathRequest BodyNotes
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

StatusIsSellableNext Possible StatesNotes
draftfalsepublished, unlistedInitial state
publishedtrueunlisted, archivedLive on storefront
unlistedfalsepublished, archivedHidden but accessible via direct link
archivedfalse- (terminal)No longer accessible

11.1.7 Product Kind Availability Behavior

productKindAvailability semantics
physicalStock-tracked via product_inventory. availableQuantity reflects live inventory.
digitalAlways available. canAddToCart: true, availableQuantity: null, maxPurchasableQuantity: 99.
care_packageAlways available. Same semantics as digital.

11.1.8 Error Response Variations

HTTPerrorCodeMessageScenario
400PRODUCT_SP_EXCEEDS_MRPSelling price cannot exceed MRP.SP > MRP on create/update
400PRODUCT_INVALID_STATUS_TRANSITIONInvalid status transition.Invalid status change
400PRODUCT_CREATE_FAILEDFailed to create product.Insert returned no row
400PRODUCT_INVALID_SORTInvalid sort field.Unsupported sort value
400PRODUCT_MISSING_REQUIRED_FIELDRequired field is missing.title, productKind, productTypeId, mrp, sp, categoryId
404PRODUCT_NOT_FOUNDProduct not found.Invalid product ID
404PRODUCT_CATEGORY_NOT_FOUNDCategory not found.Invalid categoryId
404PRODUCT_TAG_NOT_FOUNDOne or more tags not found.Invalid tag IDs
409PRODUCT_SLUG_EXISTSProduct with this slug already exists.Duplicate slug
429RATE_LIMIT_EXCEEDEDToo many requests.Rate limit exceeded

11.1.9 Cache Key Patterns

Cache KeyPatternTTL (seconds)Invalidation
Customer product listcatalog:products:list:pagination:"page"-size:"size"-sort:"sort"-order:"order"-filters...300Admin create/update/delete
Product detail by slugcatalog:product:slug:"slug"600Admin update/delete
Featured products listcatalog:products:featured:pagination:"page"-size:"size"300Admin create/update/delete featured
Admin product listcatalog:products:admin:list:pagination:"page"-size:"size"-filters...60Admin mutations
New arrivalscatalog:products:new-arrivals:withinDays-"-categoryId-"-brandId-"..."`300Admin create/update/delete
Popular productscatalog:products:popular:categoryId-"-brandId-"..."300Admin create/update/delete
Trending productscatalog:products:trending:categoryId-"-brandId-"..."300Admin create/update/delete
Best sellerscatalog:products:best-sellers:categoryId-"-brandId-"..."300Admin create/update/delete

11.1.10 Rate Limit Patterns

Route FamilyKey PatternLimit
Customer listrl:catalog:products:customer:list-ip:"ip"60 req/min
Customer detailrl:catalog:products:customer:detail-ip:"ip"120 req/min
Featured listrl:catalog:products:featured-ip:"ip"60 req/min
Discovery endpointsrl:catalog:products:discovery-ip:"ip"60 req/min
Admin listrl:catalog:products:admin:list-key:"adminId"60 req/min
Admin mutationsrl: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

FieldTypeRequiredValidationNotes
titlestringNo1-200 charsProduct title
slugstringNo1-100 chars, URL-safeURL slug
descriptionstringNoMax 5000 charsFull description
shortSummarystringNoMax 500 charsBrief summary
productKindenumNophysical | digital | care_packageDetermines availability behavior and fulfillment type
productTypeIdintegerNoPositiveFK to product_type.id
specificationsobjectNoJSON objectFlexible per-product-type specs (e.g. processor, ram, coverage)
isBestSellerbooleanNobooleanBestseller product flag
mrpnumberNoPositive integerMaximum retail price
spnumberNoPositive integer, not more than mrpSelling price
categoryIdintegerNoPositiveCategory reference
tagIdsnumber[]NoValid unique positive tag IDsTag references
thumbnailUrlstringNoValid URLThumbnail image
galleryUrlsstring[]NoValid URLsProduct gallery images
metadataobjectNoJSON objectProduct metadata
mainFileUrlstringNoValid URLMain downloadable/media file URL
previewFileUrlstringNoValid URLPreview file URL
attachmentsobject[]Noname/url/type fieldsAdditional asset attachments
statusenumNodraft|published|unlisted|archivedProduct status
isSellablebooleanNo-Sellable flag

QueryProductsDto / QueryProductsAdminDto

FieldTypeRequiredNotes
pagenumberNoPage number (default 1)
sizenumberNoPage size (default 20, max 100)
paginationbooleanNoEnable pagination (default true)
searchstringNoSearch query
categoryIdnumberNoFilter by category
productKindenumNoFilter by physical | digital | care_package
productTypeIdnumberNoFilter by product type ID
tagIdnumberNoFilter by tag
brandIdnumberNoFilter by brand ID
brandSeriesIdnumberNoFilter by brand series ID
isBestSellerbooleanNoFilter best-seller products
isTrendingbooleanNoFilter trending products
minPricenumberNoMinimum selling price (inclusive)
maxPricenumberNoMaximum selling price (inclusive)
minRatingnumberNoMinimum average rating (1-5)
sortstringNoSort field
orderstringNoSort order (asc/desc)
statusenumNo(admin only) Filter by status
isSellablebooleanNo(admin only) Filter sellable

ProductListItemDto

FieldTypeNotes
idnumberProduct ID
titlestringProduct title
slugstringURL slug
shortSummarystringBrief summary
thumbnailUrlstringThumbnail image URL
productTypeIdnumberFK to product_type.id
productTypeobject{ id, name, slug } product type summary
productKindenumphysical | digital | care_package
mrpnumberMaximum retail price (paisa)
spnumberSelling price (paisa)
discountnumberMRP - SP
isBestSellerbooleanBestseller flag
averageRatingnumberAverage approved rating (null when none)
reviewCountnumberCount of approved reviews (0 when none)
categoryIdnumberCategory reference
categoryobject{ name, slug } — omitted when categoryId is null
brandobject{ id, name, slug, imageUrl } — null when no brand

ProductResponseDto

FieldTypeNotes
idnumberProduct ID
titlestringProduct title
slugstringURL slug
shortSummarystringBrief summary
descriptionstringFull description
productKindenumphysical | digital | care_package
productTypeIdnumberFK to product_type.id
productTypeobject{ id, name, slug } product type summary
isBestSellerbooleanBestseller flag
specificationsobjectFlexible per-product-type specs
mrpnumberMaximum retail price (paisa)
spnumberSelling price (paisa)
discountnumberMRP - SP
currencystringCurrency code (e.g. USD)
totalReviewsnumberTotal approved review count
averageRatingnumberAverage approved rating
reviewStatusstring"Reviews available" or "No reviews yet"
thumbnailUrlstringThumbnail image URL
galleryUrlsstring[]Product gallery images
isSellablebooleanSellable visibility
categoryIdnumberCategory reference
categoryobject{ id, name, slug }
brandobjectBrandSummaryDto { id, name, slug, imageUrl? } — null when no brand
tagsarrayProductTagSummaryDto[]
createdAtstringISO 8601 timestamp
updatedAtstringISO 8601 timestamp

FetchNewArrivalsDto

FieldTypeRequiredDefaultNotes
withinDaysnumberNo301-365. Window for product creation date.
categoryIdnumberNo-Filter by category.
brandIdnumberNo-Filter by brand.
pagenumberNo1Page number.
sizenumberNo20Page size (max 100).

FetchDiscoveryDto (shared by popular/trending)

FieldTypeRequiredDefaultNotes
categoryIdnumberNo-Filter by category.
brandIdnumberNo-Filter by brand.
pagenumberNo1Page number.
sizenumberNo20Page 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=true and status=published products; 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.
  • 429 rate limit exceeded on customer list/search with sliding window Redis keys.
  • New arrivals endpoint with withinDays=7 returns only products created within the last 7 days, sorted newest first.
  • Popular endpoint excludes products with expired isBestSellerExpiresAt, even when isBestSeller=true.
  • Trending endpoint excludes products with expired trendingExpiresAt, even when isTrending=true.
  • Brand filter (?brandId=2) correctly scopes discovery endpoint results.
  • Filter by minRating=4 on customer list returns only products with average approved rating >= 4.
  • Sort by rating on customer list returns products ordered by descending average rating.
  • PRODUCT_BRAND_NOT_FOUND returned for invalid brand ID in discovery filters.
  • PRODUCT_INVALID_WITHIN_DAYS returned for withinDays value 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 by createdAt 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.

  • 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 by createdAt DESC.
  • Parameters: categoryId, brandId, page, size.
  • DTO: FetchDiscoveryDto
  • Cache prefix: catalog:products:popular:
  • 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 by createdAt 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=true products 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_history for historical slug support.

See Also