Catalogue
Products and everything that classifies them. The backend catalogs module owns it; the storefront shows it; the admin manages it.
How this page was verified
Read on 2026-09-15 from the backend src/catalogs/, the storefront /products routes and the admin /admin/products area.
The model
A product groups articles. An article is one buyable variant (a SKU with size and colour); stock is tracked per article. Products are classified several ways:
- head → type → subtype — the primary product tree (
product_heads,product_types,product_subtypes). The storefront PLP defaults its head toNEXT_PUBLIC_BAG_ID. - category → sub-category (
categories,sub_categories). - brand, series, collaboration, tags, color codes, icons.
Supporting tables: narratives, size_charts, product_on_sales, product_recommendations, and image/banner/video tables. Table names are the @Entity({ name }) values; the full list is on the Database page. Most text fields are bilingual (nameId/nameEn, …).
Backend
src/catalogs/ — resolvers split by audience:
- CMS (
products.resolverand per-classifier resolvers):products,product,productArticles,productArticle,productArticleQuantity, delete mutations, and CRUD for heads/types/subtypes, categories, brands, series, collaborations, tags, color-codes. Product create/update is REST, not GraphQL — the GraphQL create/update are commented out; the admin posts multipart tocatalogs/products(and/:productId/articles,/update-quantity). - Storefront (
storefront-*.resolver.ts,front*ops):frontProducts,frontProduct,frontSearchProducts,frontProductOfMetaUrl,frontProductOfShortcode,frontProductRecommendations,frontProductNewArrivals,frontProductOnSales,frontProductSizes,frontCategories,frontProductHeads,frontProductTypes,frontSpecialProductType,frontSeries*,frontCollaboration(s),frontTags,getPopularSearchKeywords.
Stock comes from the WMS through WmsService — see Stock (WMS).
Storefront
- PLP
/products(src/app/products/page.tsx) reads?head=&subcategory=&type=; UI insrc/modules/products/components/, data hooksrc/hooks/useProducts.tsx, GraphQL insrc/lib/products/,src/lib/product-heads/,src/lib/product-types/. - PDP
/products/[slug](src/app/products/[slug]/page.tsx, with a serverlayout.tsxthat fetches bymetaUrlfor SEO). UI insrc/modules/product-details/components/— media, variants, add-to-cart/buy-now (product-cart-form.tsx), prices, tier discount, flash-sale banner, reviews, share. Image zoom viareact-zoom-pan-pinch. - Short link
/s/[shortcode]resolves a product by shortcode and redirects to/products/{metaUrl}. - Related listings:
/series,/collaboration,/new-arrival,/sale,/search(behindNEXT_PUBLIC_FEAT_SEARCH).
Admin
/admin/products (src/app/admin/products/page.tsx) with add-product, edit-product, and variants under [slug]/add-variant / edit-variant. The form is tabbed (Detail + SEO) and submits FormData to catalogs/products via axios (src/lib/products/product.rest.ts), because it uploads images. Classifier CRUD lives in sibling areas: heads-types, categories, sub-categories, brands, tags, series, collaborations, color-codes, icons, category-icons.
Gotchas
- Product write is REST, read is GraphQL. Do not look for a
createProductmutation — it is a REST controller. - Stock is not stored on the product. Quantity comes from the WMS;
productArticleQuantity/frontProductSizesreflect it. See Stock (WMS). synchronize: true— a new classifier or field is an entity change, not a migration. See Database.