Brand Series Module API & Integration Guide
HTTP contract reference for catalog brand-series admin and customer/mobile APIs.
Audience: Mobile/web frontend developers, admin panel developers Scope: Brand series management, storefront series listing and detail
Brand Series Module - API & Integration Guide
1. How to Read / Quick Metadata
- Module:
BrandSeries - Auth models:
- Customer/mobile routes:
Public(no authentication required) - Admin routes:
JwtAuthGuard + RoleGuard + @Permissions(...)
- Customer/mobile routes:
- Primary base URLs:
- Admin:
/api/admin/brand-series - Customer:
/api/brand-series - Mobile-composed customer:
/api/mobile/brand-series
- Admin:
- Response envelope: successful endpoints return
ResponseDto<T> - Swagger tags used by controllers:
Brand Series (Admin)Brand Series
2. High-Level Overview
Brand Series API is split into:
- admin CRUD endpoints for series management (create, read, update, delete, reorder)
- public read-only endpoints for storefront listing and detail
Admin endpoints require specific BrandSeries_* permissions and are used by the admin panel. Customer endpoints are public and return only visible (isActive = true) series.
Mobile-composed endpoints mirror customer endpoints exactly and are available under /api/mobile/brand-series/... via MobileModule routing composition.
3. Core Concepts and Terminology
- brand series: a named collection of products within a brand, scoped to a category (e.g. "Dell XPS Series" under Brand "Dell" and Category "Laptops"). Stored in the
brand_seriestable. - slug: URL-safe identifier auto-generated from series name with suffix resolution on conflict. Used as the lookup key for public detail endpoints.
- visibility:
isActiveboolean controls whether a series appears in public endpoints. Admin endpoints always return all series regardless of visibility. - order: integer field for manual storefront sequencing. Lower values appear first. Default sort is
order ASC, name ASC. - product count: count of published and sellable products linked to this series via
product.series_id. Only available in customer detail endpoint. - reorder: bulk update operation that accepts an array of
{ id, order }tuples to set the display order for multiple series at once.
Current admin operations:
- List, Detail, Create, Update, Delete, Reorder
Current customer operations:
- List (with brandId/categoryId filters), Detail by slug
4. Route Summary
4.1 Admin
| Method | Path | Permission |
|---|---|---|
GET | /api/admin/brand-series | BrandSeries_READ |
GET | /api/admin/brand-series/:id | BrandSeries_READ |
POST | /api/admin/brand-series | BrandSeries_CREATE |
PATCH | /api/admin/brand-series/:id | BrandSeries_UPDATE |
PUT | /api/admin/brand-series/reorder | BrandSeries_UPDATE |
DELETE | /api/admin/brand-series/:id | BrandSeries_DELETE |
4.2 Customer / Mobile
| Method | Path | Visibility filter |
|---|---|---|
GET | /api/brand-series | isActive = true |
GET | /api/brand-series/:slug | isActive = true |
Mobile-composed equivalents are available under /api/mobile/brand-series/....
5. Route Details
5.1 Admin List
| Aspect | Details |
|---|---|
| Endpoint | GET /api/admin/brand-series |
| Auth | JwtAuthGuard + RoleGuard + BrandSeries_READ |
| Request query | FetchBrandSeriesAdminDto (extends QueryDto) |
| Response | ResponseDto<BrandSeriesAdminResponseDto[]> (paginated) |
| Errors | 403 insufficient permissions |
Supports filtering by brandId, categoryId, and isActive as query parameters.
5.2 Admin Detail
| Aspect | Details |
|---|---|
| Endpoint | GET /api/admin/brand-series/:id |
| Auth | JwtAuthGuard + RoleGuard + BrandSeries_READ |
| Request | path: id (int) |
| Response | ResponseDto<BrandSeriesAdminResponseDto> |
| Errors | 404 BRAND_SERIES_NOT_FOUND |
5.3 Admin Create
| Aspect | Details |
|---|---|
| Endpoint | POST /api/admin/brand-series |
| Auth | JwtAuthGuard + RoleGuard + BrandSeries_CREATE |
| Request body | CreateBrandSeriesDto |
| Response | ResponseDto<BrandSeriesAdminResponseDto> |
| Errors | 404 BRAND_NOT_FOUND / CATEGORY_NOT_FOUND, 409 BRAND_SERIES_DUPLICATE |
Validates brand and category existence before creating. Auto-generates slug from name with suffix resolution on conflict.
5.4 Admin Update
| Aspect | Details |
|---|---|
| Endpoint | PATCH /api/admin/brand-series/:id |
| Auth | JwtAuthGuard + RoleGuard + BrandSeries_UPDATE |
| Request | path: id (int), body: UpdateBrandSeriesDto |
| Response | ResponseDto<BrandSeriesAdminResponseDto> |
| Errors | 404 BRAND_SERIES_NOT_FOUND, 409 BRAND_SERIES_DUPLICATE |
Partial update. If name changes, slug is regenerated and old+new detail cache is invalidated.
5.5 Admin Reorder
| Aspect | Details |
|---|---|
| Endpoint | PUT /api/admin/brand-series/reorder |
| Auth | JwtAuthGuard + RoleGuard + BrandSeries_UPDATE |
| Request body | ReorderBrandSeriesDto { items: [{ id: number, order: number }] } |
| Response | ResponseDto (no data payload) |
| Errors | - |
Updates each item's order field individually. Invalidates list cache on completion.
5.6 Admin Delete
| Aspect | Details |
|---|---|
| Endpoint | DELETE /api/admin/brand-series/:id |
| Auth | JwtAuthGuard + RoleGuard + BrandSeries_DELETE |
| Request | path: id (int) |
| Response | ResponseDto<BrandSeriesAdminResponseDto> |
| Errors | 404 BRAND_SERIES_NOT_FOUND |
Returns the deleted series data. Invalidates list and detail cache.
5.7 Customer List
| Aspect | Details |
|---|---|
| Endpoint | GET /api/brand-series or /api/mobile/brand-series |
| Auth | Public |
| Request query | FetchBrandSeriesDto (extends QueryDto) |
| Response | ResponseDto<BrandSeriesListItemDto[]> (paginated) |
| Errors | - |
Only returns series where isActive = true. Supports brandId and categoryId query filters.
5.8 Customer Detail
| Aspect | Details |
|---|---|
| Endpoint | GET /api/brand-series/:slug or /api/mobile/brand-series/:slug |
| Auth | Public |
| Request | path: slug (string) |
| Response | ResponseDto<BrandSeriesDetailDto> |
| Errors | 404 BRAND_SERIES_NOT_FOUND |
Includes productCount — number of published and sellable products linked to this series.
6. Query Parameters
6.1 Admin list (GET /api/admin/brand-series)
| Param | Type | Default | Description |
|---|---|---|---|
page | number | 1 | Page number (1-indexed) |
size | number | 20 | Items per page (max 100) |
pagination | boolean | true | Enable/disable pagination |
sort | string | updatedAt | Sort field |
order | asc | desc | desc | Sort direction |
search | string | optional | Text search |
brandId | number | optional | Filter by brand id |
categoryId | number | optional | Filter by category id |
isActive | boolean | optional | Filter by visibility status |
6.2 Customer list (GET /api/brand-series)
| Param | Type | Default | Description |
|---|---|---|---|
page | number | 1 | Page number (1-indexed) |
size | number | 20 | Items per page (max 100) |
pagination | boolean | true | Enable/disable pagination |
sort | string | updatedAt | Sort field |
order | asc | desc | desc | Sort direction |
search | string | optional | Text search |
brandId | number | optional | Filter by brand id |
categoryId | number | optional | Filter by category id |
Note: Customer list endpoint always applies isActive = true as a hard filter regardless of query params. The service-level default sort is order ASC, name ASC (hardcoded in both admin and customer service).
7. Response Shape Examples
7.1 List item (admin or customer)
{
"id": 1,
"brandId": 3,
"brandName": "Dell",
"brandSlug": "dell",
"categoryId": 5,
"categoryName": "Laptops",
"categorySlug": "laptops",
"name": "Dell Laptops",
"slug": "dell-laptops",
"description": "Premium Dell laptop collection",
"imageUrl": "https://cdn.example.com/dell-laptops.jpg",
"isActive": true,
"order": 0,
"createdAt": "2026-06-09T10:00:00.000Z"
}7.2 Customer detail (includes productCount)
{
"id": 1,
"brandId": 3,
"brandName": "Dell",
"brandSlug": "dell",
"categoryId": 5,
"categoryName": "Laptops",
"categorySlug": "laptops",
"name": "Dell Laptops",
"slug": "dell-laptops",
"description": "Premium Dell laptop collection",
"imageUrl": "https://cdn.example.com/dell-laptops.jpg",
"isActive": true,
"order": 0,
"createdAt": "2026-06-09T10:00:00.000Z",
"productCount": 12
}7.3 Create request payload
{
"brandId": 3,
"categoryId": 5,
"name": "Dell Laptops",
"description": "Premium Dell laptop collection",
"imageUrl": "https://cdn.example.com/dell-laptops.jpg",
"isActive": true,
"order": 0
}7.4 Update request payload (partial)
{
"name": "Dell Premium Laptops",
"isActive": false
}7.5 Reorder request payload
{
"items": [
{ "id": 1, "order": 0 },
{ "id": 2, "order": 1 },
{ "id": 3, "order": 2 }
]
}7.6 Admin response (after create/update)
{
"success": true,
"message": "Brand series created successfully",
"data": {
"id": 1,
"brandId": 3,
"brandName": "Dell",
"brandSlug": "dell",
"categoryId": 5,
"categoryName": "Laptops",
"categorySlug": "laptops",
"name": "Dell Laptops",
"slug": "dell-laptops",
"description": "Premium Dell laptop collection",
"imageUrl": "https://cdn.example.com/dell-laptops.jpg",
"isActive": true,
"order": 0,
"createdAt": "2026-06-09T10:00:00.000Z",
"updatedAt": "2026-06-09T10:00:00.000Z"
}
}7.7 Paginated list response
{
"success": true,
"message": "Brand series fetched successfully",
"data": [
{ "id": 1, "brandId": 3, "name": "Dell Laptops", "slug": "dell-laptops", ... },
{ "id": 2, "brandId": 3, "name": "Dell XPS", "slug": "dell-xps", ... }
],
"meta": {
"count": 5,
"page": 1,
"size": 20
}
}8. Enums
8.1 Sort fields
Brand series uses the QueryDto base sort field. Both admin and customer lists accept any valid column name as the sort parameter:
updatedAt(default)nameordercreatedAtbrandIdcategoryId
The order parameter accepts:
asc(ascending)desc(descending, default)
Note: The service layer hardcodes order ASC, name ASC for the query regardless of the sort/order query parameters. The QueryDto sort/order parameters are parsed but not currently applied to the DB query. This is the current implementation behavior.
9. Integration Diagram
10. Caching
Cache keyspaces:
catalog:brand-series:list:*- customer list responsescatalog:brand-series:detail:slug:*- customer detail by slug
TTL env:
CATALOG_CACHE_TTL_SECONDS(default300)
Invalidation triggers:
- admin write operations (create, update, delete, reorder) invalidate list cache via pattern
- update and delete additionally invalidate the affected detail cache by slug
- slug changes during update invalidate both old and new slug cache keys
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/brand-series | Admin + BrandSeries_READ | FetchBrandSeriesAdminDto (page?, size?, pagination?, brandId?, categoryId?, isActive?, sort?, order?, search?) | BrandSeriesAdminResponseDto[] paginated | List all series; supports brand/category/visibility filters |
| GET | /api/admin/brand-series/:id | Admin + BrandSeries_READ | path: id (int) | BrandSeriesAdminResponseDto | Fetches single series by id |
| POST | /api/admin/brand-series | Admin + BrandSeries_CREATE | CreateBrandSeriesDto | BrandSeriesAdminResponseDto | Creates series with auto-generated slug |
| PATCH | /api/admin/brand-series/:id | Admin + BrandSeries_UPDATE | path: id (int), body: UpdateBrandSeriesDto | BrandSeriesAdminResponseDto | Partial update; regenerates slug if name changes |
| PUT | /api/admin/brand-series/reorder | Admin + BrandSeries_UPDATE | ReorderBrandSeriesDto ({ items: [{ id: number, order: number }] }) | ResponseDto (no data) | Bulk update order values; invalidates list cache |
| DELETE | /api/admin/brand-series/:id | Admin + BrandSeries_DELETE | path: id (int) | BrandSeriesAdminResponseDto | Deletes series; returns deleted data |
| GET | /api/brand-series | Public | FetchBrandSeriesDto (page?, size?, pagination?, brandId?, categoryId?, sort?, order?, search?) | BrandSeriesListItemDto[] paginated | Visible series only; supports brand/category filters |
| GET | /api/brand-series/:slug | Public | path: slug (string) | BrandSeriesDetailDto | Visible series only; includes productCount |
11.1.1 Admin List Endpoint Query Variations
| Method | Path | Query Parameters | Notes |
|---|---|---|---|
| GET | /api/admin/brand-series | (no params) | Default: page=1, size=20, sort=updatedAt, order=desc |
| GET | /api/admin/brand-series | ?page=2&size=10 | Custom page and size |
| GET | /api/admin/brand-series | ?brandId=3 | Filter by brand |
| GET | /api/admin/brand-series | ?categoryId=5 | Filter by category |
| GET | /api/admin/brand-series | ?brandId=3&categoryId=5 | Filter by brand + category |
| GET | /api/admin/brand-series | ?isActive=true | Filter by active series only |
| GET | /api/admin/brand-series | ?isActive=false | Filter by inactive series only |
| GET | /api/admin/brand-series | ?search=Dell | Search by name |
| GET | /api/admin/brand-series | ?pagination=false | Returns all records (no pagination) |
11.1.2 Customer/Mobile List Endpoint Query Variations
| Method | Path | Query Parameters | Notes |
|---|---|---|---|
| GET | /api/brand-series | (no params) | Default: page=1, size=20; visible only |
| GET | /api/brand-series | ?brandId=3 | Filter by brand |
| GET | /api/brand-series | ?categoryId=5 | Filter by category |
| GET | /api/brand-series | ?brandId=3&categoryId=5 | Filter by brand + category |
| GET | /api/brand-series | ?page=1&size=5 | Custom pagination |
| GET | /api/brand-series | ?search=ThinkPad | Search by name |
| GET | /api/brand-series | ?pagination=false | Returns all visible records |
11.1.3 Create Series Request Variations
| Method | Path | Request Body | Notes |
|---|---|---|---|
| POST | /api/admin/brand-series | { "brandId": 3, "categoryId": 5, "name": "Dell Laptops" } | Minimal create; defaults: isActive=true, order=0 |
| POST | /api/admin/brand-series | { "brandId": 3, "categoryId": 5, "name": "Dell Laptops", "description": "Premium collection", "imageUrl": "https://cdn.example.com/img.jpg", "isActive": true, "order": 1 } | Full create with all optional fields |
11.1.4 Update Series Request Variations
| Method | Path | Body | Notes |
|---|---|---|---|
| PATCH | /api/admin/brand-series/1 | { "name": "Dell Premium Laptops" } | Update name (slug regenerated) |
| PATCH | /api/admin/brand-series/1 | { "isActive": false } | Toggle visibility |
| PATCH | /api/admin/brand-series/1 | { "description": null } | Clear description |
| PATCH | /api/admin/brand-series/1 | { "order": 5 } | Change display order |
| PATCH | /api/admin/brand-series/1 | { "categoryId": 10 } | Reassign to different category |
11.1.5 Error Response Variations
| HTTP | errorCode | Message | Scenario |
|---|---|---|---|
| 404 | BRAND_SERIES_NOT_FOUND | Brand series not found | Invalid series id/slug |
| 404 | BRAND_NOT_FOUND | Brand not found. | Invalid brandId in create |
| 404 | CATEGORY_NOT_FOUND | Category not found. | Invalid categoryId in create |
| 409 | BRAND_SERIES_DUPLICATE | A series for this brand and category already exists | Duplicate (brandId, categoryId) pair |
| 409 | BRAND_SERIES_SLUG_EXISTS | Brand series slug already exists | Slug collision (rare; slug generation is conflict-aware) |
| 403 | - | Forbidden | Missing required BrandSeries_* permission |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests. Please try again later. | Rate limit hit (global admin throttle) |
11.1.6 Cache Key Patterns
| Cache Key | Pattern | TTL (seconds) | Invalidation |
|---|---|---|---|
| Customer list | catalog:brand-series:list:brand:<brandId>-cat:<catId>-page:<page>-size:<size> | 300 (or CATALOG_CACHE_TTL_SECONDS) | Any admin write (create, update, delete, reorder) |
| Customer detail | catalog:brand-series:detail:slug:<slug> | 300 (or CATALOG_CACHE_TTL_SECONDS) | Series update or delete affecting this slug |
11.1.7 DTO Field Reference
CreateBrandSeriesDto
| Field | Type | Required | Validation | Notes |
|---|---|---|---|---|
| brandId | number | Yes | Min(1) | FK to brand.id; validated on write |
| categoryId | number | Yes | Min(1) | FK to categories.id; validated on write |
| name | string | Yes | 1-255 chars, trimmed | Display name; slug auto-generated from this |
| description | string | No | Max 1000 chars, trimmed | Optional description for storefront display |
| imageUrl | string | No | Valid URL with protocol | Optional image URL for series card |
| isActive | boolean | No | - | Defaults to true |
| order | number | No | Min(0) | Defaults to 0 |
UpdateBrandSeriesDto
UpdateBrandSeriesDto extends PartialType(CreateBrandSeriesDto) -- all fields from CreateBrandSeriesDto are optional.
| Field | Type | Required | Notes |
|---|---|---|---|
| brandId | number | No | Will re-validate brand if provided |
| categoryId | number | No | Will re-validate category if provided |
| name | string | No | Slug regenerated if changed |
| description | string | No | Set to null to clear |
| imageUrl | string | No | Set to null to clear |
| isActive | boolean | No | Toggle visibility |
| order | number | No | Change display order |
ReorderBrandSeriesDto
| Field | Type | Required | Validation | Notes |
|---|---|---|---|---|
| items | ReorderItemDto[] | Yes | Array of objects | Each item must have id and order |
ReorderItemDto
| Field | Type | Required | Validation | Notes |
|---|---|---|---|---|
| id | number | Yes | Min(1) | Series id to reorder |
| order | number | Yes | Min(0) | New order position |
BrandSeriesAdminResponseDto
| Field | Type | Notes |
|---|---|---|
| id | number | Primary key |
| brandId | number | FK to brand |
| brandName | string | Resolved brand name (from DB join) |
| brandSlug | string | Resolved brand slug (from DB join) |
| categoryId | number | FK to category |
| categoryName | string | Resolved category name (from DB join) |
| categorySlug | string | Resolved category slug (from DB join) |
| name | string | Series display name |
| slug | string | URL-safe identifier |
| description | string | null | Optional description |
| imageUrl | string | null | Optional image URL |
| isActive | boolean | Visibility flag |
| order | number | Display order |
| createdAt | Date | Creation timestamp |
| updatedAt | Date | Last update timestamp |
BrandSeriesListItemDto
Same as BrandSeriesAdminResponseDto without the updatedAt field.
BrandSeriesDetailDto
Extends BrandSeriesListItemDto with:
| Field | Type | Notes |
|---|---|---|
| productCount | number | Count of published + sellable products linked to this series |
12. Testing Scenarios
12.1 Admin Create Series
Test Case: Create a new brand series with valid brand and category
- Admin has
BrandSeries_CREATEpermission. - POST
/api/admin/brand-serieswith{ "brandId": 3, "categoryId": 5, "name": "Dell Laptops" }. - Expect
201withBrandSeriesAdminResponseDtocontaining auto-generated slug. - Verify slug is
dell-laptops. - Verify
isActivedefaults totrue. - Verify
orderdefaults to0.
12.2 Admin Create Duplicate Series
Test Case: Create series with duplicate brand-category pair
- Series for brand 3 and category 5 already exists.
- POST
/api/admin/brand-serieswith{ "brandId": 3, "categoryId": 5, "name": "Another Series" }. - Expect
409witherrorCode: "BRAND_SERIES_DUPLICATE".
12.3 Admin Update Series Name
Test Case: Update series name triggers slug regeneration
- Series exists with slug
dell-laptops. - PATCH
/api/admin/brand-series/1with{ "name": "Dell Premium Laptops" }. - Expect
200with updated slugdell-premium-laptops. - Verify old cache key
catalog:brand-series:detail:slug:dell-laptopsis invalidated. - Verify new cache key
catalog:brand-series:detail:slug:dell-premium-laptopsis populated on next read.
12.4 Admin Reorder Series
Test Case: Reorder multiple series at once
- Three series exist with orders 0, 1, 2.
- PUT
/api/admin/brand-series/reorderwith{ "items": [{ "id": 3, "order": 0 }, { "id": 1, "order": 1 }, { "id": 2, "order": 2 }] }. - Expect
200with success message. - Verify GET list returns series in the new order.
12.5 Customer List Series by Brand
Test Case: List visible series for a specific brand
- Brand 3 has 5 series, 3 of which are active.
- GET
/api/brand-series?brandId=3. - Expect
200with paginated list of 3 active series. - Verify all items have
isActive = true. - Verify pagination meta is accurate.
12.6 Customer Detail by Slug
Test Case: Get series detail with product count
- Series "dell-laptops" exists with 12 published sellable products.
- GET
/api/brand-series/dell-laptops. - Expect
200withBrandSeriesDetailDto. - Verify
slugisdell-laptops. - Verify
productCountis12.
12.7 Customer Detail Not Found
Test Case: Request inactive series via customer endpoint
- Series exists with
isActive = falseand sluginactive-series. - GET
/api/brand-series/inactive-series. - Expect
404witherrorCode: "BRAND_SERIES_NOT_FOUND".
13. Error Handling
All brand series API errors return the standard error envelope:
{
"statusCode": 404,
"errorCode": "BRAND_SERIES_NOT_FOUND",
"message": "Brand series not found.",
"timestamp": "2026-06-09T10:00:00.000Z",
"path": "/api/brand-series/invalid-slug"
}Representative error map:
| HTTP | errorCode | Message |
|---|---|---|
| 404 | BRAND_SERIES_NOT_FOUND | Brand series not found. |
| 404 | BRAND_NOT_FOUND | Brand not found. |
| 404 | CATEGORY_NOT_FOUND | Category not found. |
| 409 | BRAND_SERIES_DUPLICATE | A series for this brand and category already exists. |
| 409 | BRAND_SERIES_SLUG_EXISTS | Brand series slug already exists. |
| 403 | - | Forbidden resource. |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests. Please try again later. |
| 503 | ERR_CONFIG_INVALID | Configuration validation failed. |
Time fields in this module are stored as timezone-aware values and should be handled as ISO-8601 instants by API consumers.
See Also
- Feature Guide: See Brand Series - Feature List Section 6 (State Models) for lifecycle details.
- Backend Reference: See Brand Series - Backend Documentation Section 3 (Data Model) for schema and indexes.
- Brand Module: See
/docs/developer/catalog/brandfor brand-level API documentation. - Product Module: See
/docs/developer/productfor product-to-series linkage API.