Implement pagination response handling and related enhancements

- Introduced `@Pagination()` decorator to mark list endpoints for pagination.
- Added `TransformInterceptor` to wrap responses in a standardized format `{ data, meta }`.
- Created pagination-related utility functions and constants for managing pagination logic.
- Defined `PaginationQueryDto` for handling pagination query parameters.
- Established `PaginationMetaDto` for OpenAPI documentation of pagination metadata.
- Updated existing controller and service structures to support pagination in responses.
- Added unit tests for pagination utilities and interceptor to ensure correct functionality.
This commit is contained in:
shancheas
2026-08-21 15:56:54 +07:00
parent d01fd6e2ef
commit 0550cbe764
28 changed files with 1177 additions and 19 deletions
+55
View File
@@ -0,0 +1,55 @@
---
description: Main modules use separate read and write controllers with list/detail, CRUD, status, bulk, and CSV import
globs: "src/modules/**/*.ts"
alwaysApply: false
---
# Read / Write Controllers
## Structure
Every **main** (CRUD) feature module under `src/modules/` MUST expose at least:
- `*-read.controller.ts`
- `*-write.controller.ts`
Same resource path and `@ApiTags`. Example: `@Controller('shipments')`.
Exempt: auth and similar non-CRUD modules (single controller is fine).
```typescript
@Module({
controllers: [ShipmentsReadController, ShipmentsWriteController],
providers: [ShipmentsService, ShipmentsRepository],
})
export class ShipmentsModule {}
```
Register static write paths (`import`, `bulk-delete`, `bulk-status`) **before** parameterized `:id` routes so they never collide.
## Read controller
1. `GET /` — list
2. `GET /:id` — detail
List requirements:
- Query filters for the resources own attributes **plus** `search` (case-insensitive match on the modules searchable text columns; AND with other filters)
- Shared pagination query (`page`/`limit` or `offset`/`limit`) via `PaginationQueryDto`
- Handler **must** use `@Pagination()` and return `{ data, total }` — never build `meta` here (see `.cursor/rules/pagination-response.mdc`)
- Service `visibleFields` whitelist: default **all non-secret** attributes; modules may narrow. Project in the **service**, not the controller
- List query must be extendable (e.g. `extendListQuery(qb, filters)` on the repository/service) so joins/extra predicates can be added without forking list
## Write controller
1. `POST /` — create (status defaults to `draft` unless body sets a valid status)
2. `PATCH /:id` — update (**must not** change `status`; reject if `status` is present)
3. `DELETE /:id` — delete (hard delete unless the module documents otherwise)
4. `PATCH /:id/status` — body is **only** `{ status }`
5. `POST /bulk-delete` — `{ ids: string[] }`
6. `POST /bulk-status` — `{ ids: string[], status }`
7. `POST /import` — multipart CSV `file`; headers map to create fields; omitted status → `draft`; `created_by` / `updated_by` = current user; fail the batch on validation errors with row-level messages (do not echo raw invalid phones/dates beyond VO policy)
Set `created_at` / `updated_at` (`DateTime`) and `created_by` / `updated_by` (current user id) in the write path.
Document both controllers per `.cursor/rules/nestjs-swagger.mdc`.