--- description: List endpoints use @Pagination() and return { data, total }; interceptor emits { data, meta } globs: "src/**/*.controller.ts,src/common/http/**/*.ts,src/common/configure-app.ts" alwaysApply: false --- # Pagination Response Envelope Canonical list HTTP shape (TrackGo-compatible). Shared code lives in `src/common/http/response/`. ## Handler vs public response ```typescript // Handler return when marked @Pagination() interface PaginationResponse { data: T[] total: number } // Public response after TransformInterceptor interface SuccessResponse { data: T meta?: PaginationMeta } interface PaginationMeta { currentPage: number itemCount: number itemsPerPage: number totalItems: number totalPages: number } ``` ## Mandatory - Mark every list endpoint with `@Pagination()` - Return `{ data, total }` from the handler — **never** build `meta` in the service or controller - Query: `page`/`limit` or `offset`/`limit` (defaults `page=1`, `limit=10`; max limit `200`) - Use `@RawResponse()` for file downloads / health probes that must skip wrapping - Non-list handlers (detail, create, update, delete, status, import) pass through **unwrapped** ```typescript // BAD — hand-rolled meta / success wrapper return { success: true, data: items, meta: { total, page, limit } } // GOOD @Get() @Pagination() list(@Query() query: ListQueryDto): Promise> { return this.service.list(query) // { data, total } } ``` ## Forbidden - `success: boolean` response wrappers for lists - Putting `total` on `meta` instead of `totalItems` - Computing `PaginationMeta` in feature services - Documenting the internal `{ data, total }` shape in OpenAPI — document `{ data, meta }` instead `TransformInterceptor` is registered globally in `configureApp`.