Developer ResourcescatalogTag Tag Module API & Integration Guide
HTTP contract reference for catalog tag admin and customer/mobile APIs.
Audience: Mobile/web frontend developers
Scope: Tag discovery endpoints
- Module: Catalog / Tag
- Admin auth:
JwtAuthGuard + RoleGuard + @Permissions(Tags_*)
- Customer/mobile auth: public endpoints (
@Public())
- Surfaces:
- Admin:
/api/admin/tags
- Customer:
/api/tags
- Mobile:
/api/mobile/tags
Tag APIs provide label taxonomy management for admins and public lookup/list endpoints for storefront filters and discovery screens.
- Canonical slug: current slug returned by API.
- Historical slug: old slug persisted in
tag_slug_history and still resolvable.
| Method | Path | Permission |
|---|
GET | /api/admin/tags | Tags_READ |
GET | /api/admin/tags/:id | Tags_READ |
POST | /api/admin/tags | Tags_CREATE |
PATCH | /api/admin/tags/:id | Tags_UPDATE |
DELETE | /api/admin/tags/:id | Tags_DELETE |
GET | /api/tags and /api/mobile/tags | Public |
GET | /api/tags/:slug and /api/mobile/tags/:slug | Public |
| Aspect | Details |
|---|
| Endpoint | GET /api/tags or /api/mobile/tags |
| Auth | Public |
| Request | type filter, pagination |
| Response | ResponseDto<TagDto[]> |
| Errors | - |
| Aspect | Details |
|---|
| Endpoint | GET /api/tags/:slug or /api/mobile/tags/:slug |
| Auth | Public |
| Request | - |
| Response | ResponseDto<TagDto> |
| Errors | 404 not found |
- Inherited from
QueryDto: pagination, page, size, search
- Filters:
type
- Sort:
name | createdAt | updatedAt
- Order:
asc | desc
- Inherited from
QueryDto: page, size, search
- Defaults:
pagination=false, sort=name, order=asc
- Filters:
type
- Sort:
name | createdAt
"
"id": 7,
"name": "Handcrafted in Nepal",
"slug": "handcrafted-in-nepal",
"createdAt": "2026-02-21T10:00:00.000Z",
"updatedAt": "2026-02-21T10:00:00.000Z"
"
"
"name": "Handcrafted in Nepal",
"type": "general"
"
- sort enums:
- admin:
name | createdAt | updatedAt
- customer:
name | createdAt
- order:
asc | desc
| Prefix | Usage |
|---|
catalog:tags:list: | customer list cache |
catalog:tag:slug: | detail-by-slug cache |
Admin create/update/delete invalidates list and slug cache keyspaces.
| HTTP | errorCode | Condition |
|---|
| 400 | TAG_INVALID_SORT | Unsupported customer sort field |
| 404 | TAG_NOT_FOUND | Tag/id/slug not found |
| 409 | TAG_NAME_EXISTS | Duplicate tag name on create/update |
| Method | Path | Auth / Permission | Request DTO / Params | Success DTO | Notes |
|---|
| GET | /api/admin/tags | Admin + Tags_READ | QueryTagsAdminDto "" page?, size?, pagination?, search?, type?, sort?, order? "" | TagAdminListItemDto[] paginated | Admin tag list |
| GET | /api/admin/tags/:id | Admin + Tags_READ | path: id (int) | TagAdminDetailDto | Admin tag detail |
| POST | /api/admin/tags | Admin + Tags_CREATE | body: CreateTagDto "" name, slug?, type "" | TagAdminDetailDto | Creates new tag |
| PATCH | /api/admin/tags/:id | Admin + Tags_UPDATE | path: id (int), body: UpdateTagDto "" name?, slug?, type? "" | TagAdminDetailDto | Updates tag |
| DELETE | /api/admin/tags/:id | Admin + Tags_DELETE | path: id (int) | "" success: true "" | Deletes tag |
| GET | /api/tags | Public | QueryTagsDto "" page?, size?, type?, sort?, order? "" | TagListItemDto[] | Customer tag list |
| GET | /api/mobile/tags | Public | QueryTagsDto "" page?, size?, type?, sort?, order? "" | TagListItemDto[] | Mobile tag list |
| GET | /api/tags/:slug | Public | path: slug (string) | TagDetailDto | Tag detail by slug (supports historical slugs) |
| GET | /api/mobile/tags/:slug | Public | path: slug (string) | TagDetailDto | Mobile tag detail by slug |
| Method | Path | Query Parameters | Notes |
|---|
| GET | /api/admin/tags | (no params) | Default: page=1, size=20, sort=createdAt, order=desc |
| GET | /api/admin/tags | ?page=2&size=10 | Custom page and size |
| GET | /api/admin/tags | ?search=handcrafted | Search by name |
| GET | /api/admin/tags | ?type=book | Filter by book type |
| GET | /api/admin/tags | ?type=general | Filter by general type |
| GET | /api/admin/tags | ?type=newsletter | Filter by newsletter type |
| GET | /api/admin/tags | ?sort=name&order=asc | Sort by name ascending |
| GET | /api/admin/tags | ?sort=updatedAt&order=desc | Sort by last update |
| Method | Path | Query Parameters | Notes |
|---|
| GET | /api/tags | (no params) | Default: no pagination, sort=name, order=asc |
| GET | /api/tags | ?page=1&size=10 | Paginated request |
| GET | /api/tags | ?type=book | Filter by book type |
| GET | /api/tags | ?type=general | Filter by general type |
| GET | /api/tags | ?sort=name&order=asc | Sort by name |
| GET | /api/tags | ?sort=createdAt&order=desc | Sort by creation date |
| Method | Path | Path/Query Parameters | Notes |
|---|
| GET | /api/tags/handcrafted-in-nepal | slug=handcrafted-in-nepal | Current slug lookup |
| GET | /api/tags/old-slug | slug=old-slug | Historical slug fallback |
| GET | /api/mobile/tags/hand-painted | slug=hand-painted | Mobile slug lookup |
| Method | Path | Request Body | Notes |
|---|
| POST | /api/admin/tags | "" "name": "Handcrafted in Nepal", "type": "general" "" | General type tag |
| POST | /api/admin/tags | "" "name": "Investment Guide", "type": "book" "" | Book type tag |
| POST | /api/admin/tags | "" "name": "Weekly Tips", "type": "newsletter" "" | Newsletter type tag |
| Method | Path | Request Body | Notes |
|---|
| PATCH | /api/admin/tags/1 | "" "name": "Updated Name" "" | Update name only |
| PATCH | /api/admin/tags/1 | "" "slug": "new-slug" "" | Update slug |
| PATCH | /api/admin/tags/1 | "" "name": "New Name", "slug": "new-slug" "" | Update both name and slug |
| Type | Description | Common Use Cases |
|---|
general | Generic tags | General topics, broad categories |
book | Book-related tags | E-book filtering |
newsletter | Newsletter-related tags | Newsletter categorization |
| HTTP | errorCode | Message | Scenario |
|---|
| 400 | TAG_INVALID_SORT | Invalid sort field. | Unsupported sort |
| 404 | TAG_NOT_FOUND | Tag not found. | Invalid ID or slug |
| 409 | TAG_NAME_EXISTS | Tag with this name already exists. | Duplicate name |
| 409 | TAG_SLUG_EXISTS | Tag with this slug already exists. | Duplicate slug |
| Cache Key | Pattern | TTL (seconds) | Invalidation |
|---|
| Customer tag list | catalog:tags:list:type:"type"-sort:"sort"-order:"order" | 600 | Admin create/update/delete |
| Tag detail by slug | catalog:tag:slug:"slug" | 600 | Admin update/delete |
| Admin tag list | catalog:tags:admin:list:pagination:"page"-size:"size"-filters... | 60 | Admin mutations |
| Route Family | Key Pattern | Limit |
|---|
| Customer list | rl:catalog:tags:customer:list-ip:"ip" | 60 req/min |
| Customer detail | rl:catalog:tags:customer:detail-ip:"ip" | 120 req/min |
| Admin list | rl:catalog:tags:admin:list-key:"adminId" | 60 req/min |
| Admin mutations | rl:catalog:tags:admin:mutate-key:"adminId" | 30 req/min |
| Field | Type | Required | Validation | Notes |
|---|
| name | string | Yes | 1-100 chars | Tag name |
| slug | string | No | 1-100 chars, URL-safe | URL slug (auto-generated if not provided) |
| Field | Type | Required | Validation | Notes |
|---|
| name | string | No | 1-100 chars | Tag name |
| slug | string | No | 1-100 chars, URL-safe | URL slug |
| Field | Type | Required | Notes |
|---|
| page | number | No | Page number |
| size | number | No | Page size |
| pagination | boolean | No | (admin only) Enable pagination |
| search | string | No | (admin only) Search query |
| type | enum | No | Filter by tag type |
| sort | string | No | Sort field |
| order | string | No | Sort order |
- Admin creates tag with auto-generated slug from name; slug resolves correctly.
- Admin changes tag slug; old slug still resolves via historical slug fallback.
- Customer requests tag by historical slug; resolves to current tag via fallback.
- Admin attempts to create duplicate tag name; receives
409 TAG_NAME_EXISTS.
- Tag list returns in sorted order by name ascending by default.