Product Category Service
Overview
The Product Category module manages the many-to-many relationship between Products and Categories.
It defines how products are assigned to categories and enables:
- category-based product listings
- navigation structures
- filtering and grouping of products
This module does not manage category definitions or product data itself. It only manages the relationship between them.
The join table (catalog.ProductCategory) is an explicit EF entity with its own DbSet,
enabling direct, efficient queries without loading full Product or Category objects.
All operations are tenant-aware and fully audited.
Endpoints
GET
/api/v1/ProductCategories/category/{categoryId}
Returns a list of product IDs assigned to a given category.
Route parameters:
categoryId(long, required)
Behavior:
- Returns only product IDs (not full product objects)
- Supports large datasets via internal cursor-based batching
Response:
List<long>- list of product identifiers
Authorization:
[AllowAnonymous]— no token required
GET
/api/v1/ProductCategories/product/{productId}
Returns a list of category IDs assigned to a given product.
Route parameters:
productId(long, required)
Behavior:
- Returns only category IDs (not full category objects)
- Supports large datasets via internal cursor-based batching
Response:
List<long>- list of category identifiers
Authorization:
[AllowAnonymous]— no token required
POST
/api/v1/ProductCategories/product/{productId}
Assigns one or more categories to a product in a single request.
Route parameters:
productId(long, required)
Request body:
List<long>— list of category IDs to assign
Behavior:
- Product must exist
- Only valid category IDs are processed (invalid IDs are silently skipped)
- Duplicate relationships are silently skipped
- Writes an audit log entry on success
Errors:
422ifproductId <= 0or body is empty404if product does not exist
Response:
204 No Content
Authorization:
- Requires Bearer Token
- Permission:
FullManage
DELETE
/api/v1/ProductCategories/product/{productId}
Removes categories from a product. Behaviour depends on the request body.
Route parameters:
productId(long, required)
Request body:
null— removes all categories from the product[](empty list) — no-op, returns immediatelyList<long>— removes only the specified categories
Behavior:
- Uses
ExecuteDeleteAsyncfor direct DB-level delete (no entity loading) - Writes an audit log entry
Response:
204 No Content
Authorization:
- Requires Bearer Token
- Permission:
FullManage
DELETE
/api/v1/ProductCategories/category/{categoryId}
Removes all products from a specific category.
Route parameters:
categoryId(long, required)
Behavior:
- Uses
ExecuteDeleteAsyncfor direct DB-level delete - Writes an audit log entry
Response:
204 No Content
Authorization:
- Requires Bearer Token
- Permission:
FullManage
Notes
- This module manages relationships only — no product or category data is modified
- Deletions affect only the relationship, not the products or categories themselves
- Large datasets are processed in batches to avoid memory pressure
- All state-changing operations are audited