Shop It Docs
Developer ResourcescatalogCategory

Category Module Backend Documentation

Backend architecture and invariants for catalog category flows.

Summary

Category backend manages:

  • hierarchical categories with max depth 3
  • slug generation + slug history continuity
  • soft delete and restore lifecycle
  • customer/mobile category discovery
  • product kind tagging for storefront filtering

Data Model

  • category table
    • hierarchy: parent_id -> category.id
    • soft delete: deleted_at, deleted_by
    • visibility: is_visible
    • ordering: order
    • product kind: product_kind (nullable enum: physical, digital, both)
  • category_slug_history
    • tracks old slugs for legacy URL resolution

Product Kind Enum

CREATE TYPE category_product_kind AS ENUM ('physical', 'digital', 'both');

Separate from the product_kind enum on the products table. Category kind represents which storefront the category belongs to, not the product's delivery format.

NULL means the category has no kind constraint (legacy or cross-kind category).

Core Invariants

  • category cannot be its own parent
  • category cannot be moved under descendant
  • depth cannot exceed 3 levels
  • soft delete is blocked if active children exist
  • restore is blocked if parent is soft-deleted
  • parent-wise reorder allows only siblings under same parentId

Product Kind Filtering

Admin

Admin filters use exact match: WHERE product_kind = $filter.

Customer

Customer filter expands to include both categories:

WHERE product_kind = $filter OR product_kind = 'both'

This ensures that when the physical storefront queries productKind=physical, categories intended for both storefronts are also returned.

Soft Delete Lifecycle

Delete

  • endpoint: DELETE /api/admin/categories/:id
  • behavior: sets deleted_at, deleted_by
  • does not physically remove row

Restore

  • endpoint: POST /api/admin/categories/:id/restore
  • behavior:
    • validates parent and depth rules against active tree
    • resolves slug conflicts with suffix strategy (slug, slug-1, slug-2)
    • reactivates category by clearing deleted_at, deleted_by

Query Behavior

  • admin list/detail excludes soft-deleted by default
  • admin can include them via includeDeleted=true
  • customer/mobile always exclude soft-deleted
  • slug-history fallback returns only visible + active category

Caching

Category mutations invalidate:

  • catalog:categories:visible*
  • catalog:categories:list:*
  • catalog:category:slug:*

Customer list cache key includes productKind as a segment. Different kind filter values produce separate cache entries.