- Updated pagination-response and read-write-controllers documentation to include `orderBy` and `orderType` parameters for sorting results. - Introduced new `order-clause` module to handle ordering logic, including validation for order types and columns. - Enhanced `PaginationQueryDto` to support ordering fields in API requests. - Updated various repository and service classes to implement ordering in database queries. - Added unit tests for new ordering functionality and ensured existing tests cover the updated behavior. - Refactored related DTOs to include user and code relations for better data representation in responses.
57 lines
2.6 KiB
Plaintext
57 lines
2.6 KiB
Plaintext
---
|
||
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 resource’s own attributes **plus** `search` (case-insensitive match on the module’s searchable text columns; AND with other filters)
|
||
- Shared pagination query (`page`/`limit` or `offset`/`limit`) plus `orderBy`/`orderType` 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
|
||
- FK relations in list/detail (and write responses that reuse the mapper) MUST be nested objects via `pickRelation` — see `.cursor/rules/relation-response.mdc`
|
||
- 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`.
|