### Install nestjs-paginate Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md Install the nestjs-paginate package using npm. ```bash npm install nestjs-paginate ``` -------------------------------- ### Example: Standard Pagination Endpoint and Result Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md Demonstrates a typical endpoint for standard pagination with various query parameters and its corresponding JSON response structure. ```url http://localhost:3000/cats?limit=5&page=2&sortBy=color:DESC&search=i&filter.age=$gte:3&select=id,name,color,age&withDeleted=true ``` ```json { "data": [ { "id": 4, "name": "George", "color": "white", "age": 3 }, { "id": 5, "name": "Leche", "color": "white", "age": 6 }, { "id": 2, "name": "Garfield", "color": "ginger", "age": 4 }, { "id": 1, "name": "Milo", "color": "brown", "age": 5 }, { "id": 3, "name": "Kitty", "color": "black", "age": 3 } ], "meta": { "itemsPerPage": 5, "totalItems": 12, "currentPage": 2, "totalPages": 3, "sortBy": [["color", "DESC"]], "search": "i", "filter": { "age": "$gte:3" } }, "links": { "first": "http://localhost:3000/cats?limit=5&page=1&sortBy=color:DESC&search=i&filter.age=$gte:3", "previous": "http://localhost:3000/cats?limit=5&page=1&sortBy=color:DESC&search=i&filter.age=$gte:3", "current": "http://localhost:3000/cats?limit=5&page=2&sortBy=color:DESC&search=i&filter.age=$gte:3", "next": "http://localhost:3000/cats?limit=5&page=3&sortBy=color:DESC&search=i&filter.age=$gte:3", "last": "http://localhost:3000/cats?limit=5&page=3&sortBy=color:DESC&search=i&filter.age=$gte:3" } } ``` -------------------------------- ### Complex Multi-Filter Example with AND and OR Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md Demonstrates combining multiple filter conditions on a single column using `$and` (implicit) and `$or` operators for intricate query logic. ```plaintext `?filter.id=$gt:3&filter.id=$and:$lt:5&filter.id=$or:$eq:7` where column `id` is greater than `3` **and** less than `5` **or** equal to `7` ``` ```plaintext **Note:** The `$and` comparators are not required. The above example is equivalent to: `?filter.id=$gt:3&filter.id=$lt:5&filter.id=$or:$eq:7` ``` -------------------------------- ### Example: Cursor-based Pagination Endpoint and Result Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md Illustrates an endpoint utilizing cursor-based pagination and its corresponding JSON response, including cursor information for navigation. ```url http://localhost:3000/cats?limit=5&sortBy=lastVetVisit:ASC&cursor=V998328469600000 ``` ```json { "data": [ { "id": 3, "name": "Shadow", "lastVetVisit": "2022-12-21T10:00:00.000Z" }, { "id": 4, "name": "Luna", "lastVetVisit": "2022-12-22T10:00:00.000Z" }, { "id": 5, "name": "Pepper", "lastVetVisit": "2022-12-23T10:00:00.000Z" }, { "id": 6, "name": "Simba", "lastVetVisit": "2022-12-24T10:00:00.000Z" }, { "id": 7, "name": "Tiger", "lastVetVisit": "2022-12-25T10:00:00.000Z" } ], "meta": { "itemsPerPage": 5, "cursor": "V998328469600000" }, "links": { "previous": "http://localhost:3000/cats?limit=5&sortBy=lastVetVisit:DESC&cursor=V001671616800000", "current": "http://localhost:3000/cats?limit=5&sortBy=lastVetVisit:ASC&cursor=V998328469600000", "next": "http://localhost:3000/cats?limit=5&sortBy=lastVetVisit:ASC&cursor=V998328037600000" } } ``` -------------------------------- ### Basic Filter Query Examples Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md Illustrates common filter query parameters for various data types and operators, including string matching, date ranges, and null checks. ```plaintext `?filter.name=$eq:Milo` is equivalent with `?filter.name=Milo` ``` ```plaintext `?filter.age=$btw:4,6` where column `age` is between `4` and `6` ``` ```plaintext `?filter.id=$not:$in:2,5,7` where column `id` is **not** `2`, `5` or `7` ``` ```plaintext `?filter.summary=$not:$ilike:term` where column `summary` does **not** contain `term` ``` ```plaintext `?filter.summary=$sw:term` where column `summary` starts with `term` ``` ```plaintext `?filter.seenAt=$null` where column `seenAt` is `NULL` ``` ```plaintext `?filter.seenAt=$not:$null` where column `seenAt` is **not** `NULL` ``` ```plaintext `?filter.createdAt=$btw:2022-02-02,2022-02-10` where column `createdAt` is between the dates `2022-02-02` and `2022-02-10` ``` ```plaintext `?filter.createdAt=$lt:2022-12-20T10:00:00.000Z` where column `createdAt` is before iso date `2022-12-20T10:00:00.000Z` ``` ```plaintext `?filter.roles=$contains:moderator` where column `roles` is an array and contains the value `moderator` ``` ```plaintext `?filter.roles=$contains:moderator,admin` where column `roles` is an array and contains the values `moderator` and `admin` ``` -------------------------------- ### Multi-Filter Example: Date Range Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md Apply multiple filters to a single column to create complex conditions, such as filtering a date column to be within a specific range. ```plaintext `?filter.createdAt=$gt:2022-02-02&filter.createdAt=$lt:2022-02-10` where column `createdAt` is after `2022-02-02` **and** before `2022-02-10` ``` -------------------------------- ### FilterOperator Enum and Configuration Example Source: https://context7.com/ppetzold/nestjs-paginate/llms.txt Defines the FilterOperator enum for per-column filtering and provides an example configuration for NestJS Paginate, specifying sortable and filterable columns with various operators. ```typescript import { FilterOperator, FilterSuffix, FilterQuantifier, FilterComparator } from 'nestjs-paginate' // FilterOperator values: // $eq — exact match: ?filter.name=$eq:Milo (same as ?filter.name=Milo) // $not — negation suffix: ?filter.name=$not:$eq:Milo // $null — is null: ?filter.deletedAt=$null // $in — value in list: ?filter.id=$in:1,2,3 // $gt — greater than: ?filter.age=$gt:3 // $gte — greater than or equal: ?filter.age=$gte:3 // $lt — less than: ?filter.age=$lt:10 // $lte — less than or equal: ?filter.age=$lte:10 // $btw — between (inclusive): ?filter.age=$btw:3,10 // $ilike — case-insensitive LIKE: ?filter.name=$ilike:mil → WHERE name ILIKE '%mil%' // $sw — starts with: ?filter.name=$sw:Mi → WHERE name ILIKE 'Mi%' // $contains — array contains: ?filter.roles=$contains:admin,moderator // FilterSuffix: // $not — negate operator: ?filter.name=$not:$in:1,2,3 // FilterQuantifier (for to-many relations): // $any — at least one match (default): ?filter.toys.name=$any:$eq:Ball // $all — all related rows match: ?filter.toys.name=$all:$sw:Chew // $none — no related rows match: ?filter.toys.name=$none:$eq:Squeaky // FilterComparator: // $or — OR with the previous filter on the same column // $and — AND-mode for to-many relations (one EXISTS per value) // Example config wiring: const config: PaginateConfig = { sortableColumns: ['id'], filterableColumns: { id: [FilterOperator.EQ, FilterOperator.IN, FilterSuffix.NOT], age: true, // shorthand: allow every operator name: [FilterOperator.EQ, FilterOperator.ILIKE, FilterOperator.SW], deletedAt: [FilterOperator.NULL, FilterSuffix.NOT], createdAt: [FilterOperator.BTW, FilterOperator.GTE, FilterOperator.LTE], roles: [FilterOperator.CONTAINS], 'toys.name': [FilterOperator.EQ, FilterQuantifier.ALL, FilterQuantifier.NONE, FilterComparator.AND], }, } // Multi-filter (multiple conditions on the same column): // ?filter.id=$gt:3&filter.id=$lt:10 → id > 3 AND id < 10 // ?filter.id=$gt:3&filter.id=$or:$eq:1 → id > 3 OR id = 1 // ?filter.roles=$contains:admin&filter.roles=$or:$contains:moderator → roles ∋ admin OR moderator // to-many AND-mode (cat must have BOTH "Ball" AND "Mouse" toys): // ?filter.toys.name=$and:Ball&filter.toys.name=$and:Mouse ``` -------------------------------- ### JSONB Column Filtering with NestJS Paginate Source: https://context7.com/ppetzold/nestjs-paginate/llms.txt Demonstrates filtering PostgreSQL JSONB columns using dot-notation paths in NestJS Paginate. Shows configuration for sortable and filterable JSONB fields and provides query examples for various operators and database engines. ```typescript import { FilterOperator } from 'nestjs-paginate' import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm' @Entity() export class ProductEntity { @PrimaryGeneratedColumn() id: number @Column({ type: 'jsonb', nullable: true }) metadata: Record // e.g. { status: 'active', ui: { theme: 'dark' } } } // Config — expose JSONB paths as filterable/sortable columns const config: PaginateConfig = { sortableColumns: ['id', 'metadata.status', 'metadata.ui.theme'], filterableColumns: { 'metadata.status': [FilterOperator.EQ, FilterOperator.IN], 'metadata.ui.theme': [FilterOperator.EQ], }, } // Query examples: // ?filter.metadata.status=$eq:active // → WHERE metadata @> '{"status":"active"}' // // ?filter.metadata.status=$in:active,pending // → WHERE (metadata @> '{"status":"active"}' OR metadata @> '{"status":"pending"}') // // ?filter.metadata.status=$not:$in:banned,suspended // → WHERE NOT metadata @> '{"status":"banned"}' AND NOT metadata @> '{"status":"suspended"}' // // ?sortBy=metadata.ui.theme:ASC // → ORDER BY metadata #>> '{ui,theme}' ASC (PostgreSQL) // → ORDER BY JSON_UNQUOTE(JSON_EXTRACT(metadata, '$.ui.theme')) ASC (MySQL) // // Filtering through a relation that has a JSONB column: // Entity: UserEntity → settings (relation) → theme (jsonb column) // ?filter.settings.theme=$eq:dark → settings.theme @> '{"theme":"dark"}' ``` -------------------------------- ### Generate Swagger Docs with Single Decorator Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md Utilize the @PaginatedSwaggerDocs decorator as a shorthand to generate Swagger documentation for both the response body and query parameters simultaneously. This simplifies the decorator setup for paginated endpoints. ```typescript @Get() @PaginatedSwaggerDocs(UserDto, USER_PAGINATION_CONFIG) async findAll( @Paginate() query: PaginateQuery, ): Promise> { } ``` -------------------------------- ### NestJS Paginate with TypeORM Example Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md This code defines a Cat entity, a CatsService that uses nestjs-paginate to fetch paginated results from a TypeORM repository, and a CatsController to expose a paginated API endpoint. It configures sorting, searching, and filtering options for the pagination. ```typescript import { Controller, Injectable, Get } from '@nestjs/common' import { InjectRepository } from '@nestjs/typeorm' import { FilterOperator, FilterSuffix, Paginate, PaginateQuery, paginate, Paginated } from 'nestjs-paginate' import { Repository, Entity, PrimaryGeneratedColumn, Column } from 'typeorm' @Entity() export class CatEntity { @PrimaryGeneratedColumn() id: number @Column('text') name: string @Column('text') color: string @Column('int') age: number @Column({ nullable: true }) lastVetVisit: Date | null @CreateDateColumn() createdAt: string } @Injectable() export class CatsService { constructor( @InjectRepository(CatEntity) private readonly catsRepository: Repository ) {} public findAll(query: PaginateQuery): Promise> { return paginate(query, this.catsRepository, { sortableColumns: ['id', 'name', 'color', 'age'], nullSort: 'last', defaultSortBy: [['id', 'DESC']], searchableColumns: ['name', 'color', 'age'], select: ['id', 'name', 'color', 'age', 'lastVetVisit'], filterableColumns: { name: [FilterOperator.EQ, FilterSuffix.NOT], age: true, }, }) } } @Controller('cats') export class CatsController { constructor(private readonly catsService: CatsService) {} @Get() public findAll(@Paginate() query: PaginateQuery): Promise> { return this.catsService.findAll(query) } } ``` -------------------------------- ### Multi-Filter Example: Array Contains with OR Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md Combine multiple `$contains` operators on an array column using `$or` to check if the array contains any of the specified values. ```plaintext `?filter.roles=$contains:moderator&filter.roles=$or:$contains:admin` where column `roles` is an array and contains `moderator` **or** `admin` ``` -------------------------------- ### Extract Pagination Query with @Paginate() Decorator Source: https://context7.com/ppetzold/nestjs-paginate/llms.txt The `@Paginate()` decorator extracts pagination parameters from requests and returns a typed `PaginateQuery` object. It supports various query parameters like `page`, `limit`, `sortBy`, `search`, `filter`, `select`, `cursor`, and `withDeleted`. This example demonstrates its usage in a NestJS controller with TypeORM. ```typescript // cats.controller.ts import { Controller, Get } from '@nestjs/common' import { Paginate, PaginateQuery, Paginated, paginate, FilterOperator, FilterSuffix } from 'nestjs-paginate' import { InjectRepository } from '@nestjs/typeorm' import { Repository } from 'typeorm' import { CatEntity } from './cat.entity' // PaginateQuery shape resolved by the decorator: // { // page?: number ← ?page=2 // limit?: number ← ?limit=10 // sortBy?: [string, string][] ← ?sortBy=name:ASC&sortBy=age:DESC // search?: string ← ?search=milo // searchBy?: string[] ← ?searchBy=name&searchBy=color // filter?: { [col]: string | string[] } ← ?filter.age=$gte:3 // select?: string[] ← ?select=id,name,color // cursor?: string ← ?cursor=V00000000042X0000 // withDeleted?: boolean ← ?withDeleted=true // path: string ← reconstructed base URL for link generation // } @Controller('cats') export class CatsController { constructor( @InjectRepository(CatEntity) private readonly catsRepo: Repository, ) {} @Get() findAll(@Paginate() query: PaginateQuery): Promise> { return paginate(query, this.catsRepo, { sortableColumns: ['id', 'name', 'color', 'age'], searchableColumns: ['name', 'color'], filterableColumns: { age: [FilterOperator.GTE, FilterOperator.LTE, FilterOperator.BTW], name: [FilterOperator.EQ, FilterSuffix.NOT], }, }) } } // GET /cats?page=2&limit=5&sortBy=name:ASC&search=i&filter.age=$gte:3 // → Paginated with data[], meta{}, links{} ``` -------------------------------- ### Basic Swagger Documentation Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md Demonstrates how to use the default decorators `@ApiOkPaginatedResponse` and `@ApiPaginationQuery` to generate Swagger documentation for paginated endpoints. ```APIDOC ## Basic Swagger Documentation This section shows how to use the default decorators for Swagger documentation. ### Method GET ### Endpoint / ### Parameters #### Query Parameters - **page** (number) - Optional - The page number. - **limit** (number) - Optional - The number of items per page. - **sortBy** (string) - Optional - The column to sort by. - **filterBy** (string) - Optional - The column to filter by. - **search** (string) - Optional - The search term. ### Request Example ```typescript @Get() @ApiOkPaginatedResponse( UserDto, USER_PAGINATION_CONFIG, ) @ApiPaginationQuery(USER_PAGINATION_CONFIG) async findAll( @Paginate() query: PaginateQuery, ): Promise> { // Implementation } ``` ### Response #### Success Response (200) - **data** (array) - The paginated data. - **meta** (object) - Pagination metadata. - **links** (object) - Pagination links. ``` -------------------------------- ### Configure and Use `paginate` with Repository Source: https://context7.com/ppetzold/nestjs-paginate/llms.txt Defines a comprehensive `PaginateConfig` for a `Repository` to control sorting, searching, filtering, relations, and more. The `paginate` function then applies these configurations to the repository based on the incoming query. ```typescript import { paginate, PaginateConfig, PaginationType, FilterOperator, FilterSuffix, FilterQuantifier, FilterComparator, PaginationLimit, } from 'nestjs-paginate' import { Repository } from 'typeorm' // --- With a Repository --- const config: PaginateConfig = { sortableColumns: ['id', 'name', 'color', 'age'], nullSort: 'last', // NULLs sorted after non-nulls defaultSortBy: [['id', 'DESC']], searchableColumns: ['name', 'color'], select: ['id', 'name', 'color', 'age'], // restricts the default SELECT maxLimit: 50, defaultLimit: 20, where: { color: 'ginger' }, // static base filter applied always filterableColumns: { name: [FilterOperator.EQ, FilterSuffix.NOT], age: true, // allow all operators 'toys.name': [FilterOperator.IN, FilterComparator.AND], }, relations: ['toys', 'home'], withDeleted: false, allowWithDeletedInQuery: true, // lets ?withDeleted=true through relativePath: false, origin: 'https://api.example.com', multiWordSearch: false, defaultJoinMethod: 'leftJoinAndSelect', buildCountQuery: (qb) => { qb.expressionMap.joinAttributes = [] qb.select('cat.id').distinct(true) return qb }, } const result = await paginate(query, catsRepository, config) // result.data → CatEntity[] // result.meta → { itemsPerPage, totalItems, currentPage, totalPages, sortBy, search, filter, ... } // result.links → { first, previous, current, next, last } ``` -------------------------------- ### Use CURSOR Pagination Strategy Source: https://context7.com/ppetzold/nestjs-paginate/llms.txt Implements keyset pagination, which does not provide total item counts or page numbers in the meta data. The client must pass a cursor string obtained from previous responses. The `sortBy` parameter must include at least one unique column. ```typescript import { paginate, PaginationType } from 'nestjs-paginate' // CURSOR — keyset pagination; no totalItems/currentPage/totalPages in meta // Client passes ?cursor= returned in previous response links // sortBy MUST include at least one unique column (e.g. id) const cursorResult = await paginate(query, repo, { sortableColumns: ['lastVetVisit', 'id'], defaultSortBy: [['lastVetVisit', 'ASC'], ['id', 'ASC']], paginationType: PaginationType.CURSOR, }) // cursorResult.meta → { itemsPerPage, sortBy, cursor } // cursorResult.links → { previous, current, next } // Next page: GET /cats?sortBy=lastVetVisit:ASC&sortBy=id:ASC&cursor=V001671616800000V00000000042X0000 ``` -------------------------------- ### Use TAKE_AND_SKIP Pagination Strategy Source: https://context7.com/ppetzold/nestjs-paginate/llms.txt Employs TypeORM's `.take()` and `.skip()` methods for pagination. This is the default strategy and is safe to use with JOINs. ```typescript import { paginate, PaginationType } from 'nestjs-paginate' // TAKE_AND_SKIP (default) — uses TypeORM .take()/.skip(); safe with JOINs const takeSkipResult = await paginate(query, repo, { sortableColumns: ['id'], paginationType: PaginationType.TAKE_AND_SKIP, }) ``` -------------------------------- ### Simplified Swagger Documentation Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md Illustrates the use of the `@PaginatedSwaggerDocs` decorator as a shorthand for applying both response body and query parameter documentation. ```APIDOC ## Simplified Swagger Documentation This section shows how to use the `@PaginatedSwaggerDocs` decorator for a more concise Swagger documentation setup. ### Method GET ### Endpoint / ### Parameters #### Query Parameters - **page** (number) - Optional - The page number. - **limit** (number) - Optional - The number of items per page. - **sortBy** (string) - Optional - The column to sort by. - **filterBy** (string) - Optional - The column to filter by. - **search** (string) - Optional - The search term. ### Request Example ```typescript @Get() @PaginatedSwaggerDocs(UserDto, USER_PAGINATION_CONFIG) async findAll( @Paginate() query: PaginateQuery, ): Promise> { // Implementation } ``` ### Response #### Success Response (200) - **data** (array) - The paginated data. - **meta** (object) - Pagination metadata. - **links** (object) - Pagination links. ``` -------------------------------- ### Direct JSONB Column Filtering Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md Filter JSONB columns using dot notation to access nested fields. This example filters based on the `enabled` field within the `metadata` JSONB column. ```plaintext `?filter.metadata.enabled=$eq:true` where `metadata` is a JSONB column and the filter matches rows whose `metadata` object contains `{ "enabled": true }`. ``` -------------------------------- ### JSONB Column Through Relation Filtering Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md Access and filter JSONB columns within related entities using dot notation. This example filters the `theme` field in the `settings` relation's JSONB column. ```plaintext `?filter.settings.theme=$eq:dark` where `settings` is a relation whose JSONB column `theme` is filtered. ``` -------------------------------- ### Configure and Use `paginate` with QueryBuilder Source: https://context7.com/ppetzold/nestjs-paginate/llms.txt Demonstrates using the `paginate` function with a custom TypeORM `SelectQueryBuilder`. Configuration options like `sortableColumns` and `filterableColumns` can be adapted to reference columns within the query builder's aliases. ```typescript const qb = catsRepository .createQueryBuilder('cats') .leftJoinAndSelect('cats.owner', 'owner') .where('cats.owner = :ownerId', { ownerId: 42 }) const result2 = await paginate(query, qb, { sortableColumns: ['cats.id', 'cats.name'], filterableColumns: { 'cats.name': [FilterOperator.EQ] }, }) ``` -------------------------------- ### Wildcard Column Selection Source: https://context7.com/ppetzold/nestjs-paginate/llms.txt Demonstrates how to use the wildcard character '*' in the `select` config option and `?select=` query parameter to include all columns from an entity or its relations, simplifying manual enumeration. ```APIDOC ## Wildcard column selection (`select` with `*`) The `select` config option and `?select=` query param both support wildcard `*` to expand all columns of an entity or relation, avoiding the need to enumerate every field manually. ```typescript const config: PaginateConfig = { sortableColumns: ['id'], // select all columns from CatEntity plus all columns from the toys relation select: ['*', 'toys.*'], } // Client can also use wildcards in ?select= query param: // GET /cats?select=*,toys.* ``` ``` -------------------------------- ### Paginate with AND-mode - Multiple Conditions Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md Use multiple '$and' comparators to specify that a cat must have multiple distinct related items, such as different toys or friends. ```url GET /cats?filter.toys.name=$and:Ball&filter.toys.name=$and:Mouse ``` ```url GET /cats?filter.toys.name=$and:Ball&filter.friends.name=$and:Garfield ``` -------------------------------- ### Configure Join Methods for Relationships Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md Specify how relationships should be joined (LEFT JOIN or INNER JOIN) and their columns selected. This can be set globally or overridden per relationship. ```typescript defaultJoinMethod: 'leftJoinAndSelect', joinMethods: {age: 'innerJoinAndSelect', size: 'leftJoinAndSelect'} ``` -------------------------------- ### Use LIMIT_AND_OFFSET Pagination Strategy Source: https://context7.com/ppetzold/nestjs-paginate/llms.txt Utilizes SQL's LIMIT and OFFSET clauses for pagination. This strategy is generally faster but may lead to duplicate results when used with one-to-many JOINs. ```typescript import { paginate, PaginationType } from 'nestjs-paginate' // LIMIT_AND_OFFSET — uses SQL LIMIT/OFFSET; faster but may cause duplicates with one-to-many JOINs const limitOffsetResult = await paginate(query, repo, { sortableColumns: ['id'], paginationType: PaginationType.LIMIT_AND_OFFSET, }) ``` -------------------------------- ### Customizing Swagger UI Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md Explains how to create custom decorators to modify specific aspects of the Swagger UI, such as the description for the 'sortBy' parameter. ```APIDOC ## Customizing Swagger UI This section details how to create custom decorators to tailor the Swagger UI. ### Custom SortBy Decorator Example ```typescript export function CustomSortBy(paginationConfig: PaginateConfig) { return ApiQuery({ name: 'sortBy', isArray: true, description: `My custom sort by description`, required: false, type: 'string', }) } ``` ### Custom PaginatedSwaggerDocs Decorator Example ```typescript function CustomPaginatedSwaggerDocs>(dto: DTO, paginatedConfig: PaginateConfig) { return applyDecorators(ApiOkPaginatedResponse(dto, paginatedConfig), CustomApiPaginationQuery(paginatedConfig)) } ``` ### Usage ```typescript @Get() @CustomPaginatedSwaggerDocs(UserDto, USER_PAGINATION_CONFIG) async findAll( @Paginate() query: PaginateQuery, ): Promise> { // Implementation } ``` ``` -------------------------------- ### Configure User Entity Pagination with PaginateConfig Source: https://context7.com/ppetzold/nestjs-paginate/llms.txt Defines comprehensive pagination settings for a User entity, including sortable, searchable, and filterable columns, default sorting, limits, and relation loading. This configuration controls how clients can interact with paginated user data. ```typescript import { PaginateConfig, PaginationType, FilterOperator, FilterSuffix, FilterQuantifier, FilterComparator, } from 'nestjs-paginate' const USER_PAGINATION_CONFIG: PaginateConfig = { // REQUIRED: at least one column sortableColumns: ['id', 'email', 'createdAt', 'profile.firstName'], // Optional columns clients can search across with ?search=term searchableColumns: ['email', 'profile.firstName', 'profile.lastName'], // Restrict and gate filter operators per column filterableColumns: { id: [FilterOperator.IN, FilterOperator.EQ], email: [FilterOperator.EQ, FilterSuffix.NOT, FilterOperator.ILIKE], createdAt: [FilterOperator.GTE, FilterOperator.LTE, FilterOperator.BTW], 'roles': [FilterOperator.CONTAINS], // array column // to-many relation filters with quantifiers 'tags.name': [FilterOperator.EQ, FilterQuantifier.ALL, FilterQuantifier.NONE], // $and comparator for "must have ALL of these related values" 'tags.id': [FilterOperator.EQ, FilterComparator.AND], }, maxAndValues: 10, // cap on $and values per sub-column (default 20) // Default sort when ?sortBy is absent defaultSortBy: [['createdAt', 'DESC']], defaultLimit: 20, maxLimit: 100, // TypeORM find-options WHERE applied in addition to query filters where: { isActive: true }, // Relations to LEFT JOIN and SELECT relations: { profile: true, tags: true }, // Or use array form: // relations: ['profile', 'tags'], loadEagerRelations: false, // Partial SELECT for default response; clients can narrow with ?select= select: ['id', 'email', 'createdAt', 'profile.firstName', 'tags.name'], withDeleted: false, allowWithDeletedInQuery: true, paginationType: PaginationType.TAKE_AND_SKIP, // or LIMIT_AND_OFFSET, CURSOR relativePath: false, origin: 'https://api.example.com', ignoreSearchByInQueryParam: false, // allow ?searchBy= to narrow search ignoreSelectInQueryParam: false, // allow ?select= to narrow columns multiWordSearch: false, // true → each space-separated word is ANDed defaultJoinMethod: 'leftJoinAndSelect', joinMethods: { 'tags': 'innerJoinAndSelect', // override per relation }, } ``` -------------------------------- ### Paginate Custom Queries with Query Builder Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md Use this snippet to paginate custom queries by passing a query builder instance to the paginate function. Ensure the query builder is properly configured before passing it. ```typescript const queryBuilder = repo .createQueryBuilder('cats') .leftJoinAndSelect('cats.owner', 'owner') .where('cats.owner = :ownerId', { ownerId }) const result = await paginate(query, queryBuilder, config) ``` -------------------------------- ### paginate() - Core pagination function Source: https://context7.com/ppetzold/nestjs-paginate/llms.txt The main function that applies all pagination, sorting, filtering, searching, and selecting logic to a TypeORM `Repository` or `SelectQueryBuilder` and returns a `Promise>`. Accepts a `PaginateQuery` (from `@Paginate()`) and a `PaginateConfig` that gates what query params are honoured. ```APIDOC ## `paginate()` — Core pagination function ### Description The main function that applies all pagination, sorting, filtering, searching, and selecting logic to a TypeORM `Repository` or `SelectQueryBuilder` and returns a `Promise>`. Accepts a `PaginateQuery` (from `@Paginate()`) and a `PaginateConfig` that gates what query params are honoured. ### Usage with Repository ```typescript import { paginate, PaginateConfig, } from 'nestjs-paginate' import { Repository } from 'typeorm' const config: PaginateConfig = { sortableColumns: ['id', 'name', 'color', 'age'], nullSort: 'last', defaultSortBy: [['id', 'DESC']], searchableColumns: ['name', 'color'], select: ['id', 'name', 'color', 'age'], maxLimit: 50, defaultLimit: 20, where: { color: 'ginger' }, filterableColumns: { name: [FilterOperator.EQ, FilterSuffix.NOT], age: true, 'toys.name': [FilterOperator.IN, FilterComparator.AND], }, relations: ['toys', 'home'], withDeleted: false, allowWithDeletedInQuery: true, relativePath: false, origin: 'https://api.example.com', multiWordSearch: false, defaultJoinMethod: 'leftJoinAndSelect', buildCountQuery: (qb) => { qb.expressionMap.joinAttributes = [] qb.select('cat.id').distinct(true) return qb }, } const result = await paginate(query, catsRepository, config) // result.data → CatEntity[] // result.meta → { itemsPerPage, totalItems, currentPage, totalPages, sortBy, search, filter, ... } // result.links → { first, previous, current, next, last } ``` ### Usage with QueryBuilder ```typescript import { paginate, PaginateConfig, FilterOperator, } from 'nestjs-paginate' import { Repository } from 'typeorm' const qb = catsRepository .createQueryBuilder('cats') .leftJoinAndSelect('cats.owner', 'owner') .where('cats.owner = :ownerId', { ownerId: 42 }) const result2 = await paginate(query, qb, { sortableColumns: ['cats.id', 'cats.name'], filterableColumns: { 'cats.name': [FilterOperator.EQ] }, }) ``` ### Disable Pagination To disable pagination entirely and return all rows, set `maxLimit: -1` in the config and send `?limit=-1` in the query. The `result.meta.totalItems` will reflect all rows, and no pagination links will be generated. ### Counter-only Mode To enable counter-only mode (retrieve only the total count without data rows), send `?limit=0` in the query. The `result.data` will be an empty array (`[]`), but `result.meta.totalItems` will be correctly populated. ``` -------------------------------- ### Enable Multi-Word Search Behavior Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md When enabled, each word in the search query is treated as a separate term for more flexible matching. ```typescript multiWordSearch: false ``` -------------------------------- ### Swagger Response Decorators Source: https://context7.com/ppetzold/nestjs-paginate/llms.txt Explains how to use `@ApiOkPaginatedResponse` and `@ApiPaginationQuery` decorators to automatically generate OpenAPI schemas for paginated responses, including data, meta information, and filterable properties. ```APIDOC ## `@ApiOkPaginatedResponse` — Swagger response schema decorator Attaches an OpenAPI 200-response schema to a controller method that reflects the `Paginated` shape, including the `data[]` array typed to the DTO, and `meta.filter` properties derived from `filterableColumns`. ```typescript import { Controller, Get } from '@nestjs/common' import { ApiOkPaginatedResponse, ApiPaginationQuery, PaginatedSwaggerDocs } from 'nestjs-paginate' import { Paginate, PaginateQuery, paginate, PaginateConfig, FilterOperator } from 'nestjs-paginate' import { CatDto } from './cat.dto' import { CatEntity } from './cat.entity' const CAT_PAGINATION_CONFIG: PaginateConfig = { sortableColumns: ['id', 'name', 'age'], searchableColumns: ['name'], filterableColumns: { age: [FilterOperator.GTE, FilterOperator.LTE] }, select: ['id', 'name', 'age'], } @Controller('cats') export class CatsController { // Option 1: Two separate decorators @Get() @ApiOkPaginatedResponse(CatDto, CAT_PAGINATION_CONFIG) @ApiPaginationQuery(CAT_PAGINATION_CONFIG) async findAll(@Paginate() query: PaginateQuery) { return paginate(query, this.catsRepo, CAT_PAGINATION_CONFIG) } // Option 2: Combined shorthand decorator (equivalent to the above) @Get('v2') @PaginatedSwaggerDocs(CatDto, CAT_PAGINATION_CONFIG) async findAllV2(@Paginate() query: PaginateQuery) { return paginate(query, this.catsRepo, CAT_PAGINATION_CONFIG) } } ``` ``` -------------------------------- ### Multi-Filter Grouping Logic Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md Illustrates how multiple filters on the same column are grouped with an implicit `$and`, and how `$or` can be used to create alternative conditions. ```plaintext `...&filter.id=5&filter.id=$or:7&filter.name=Milo&...` is resolved to: `WHERE ... AND (id = 5 OR id = 7) AND name = 'Milo' AND ...` ``` -------------------------------- ### updateGlobalConfig Source: https://context7.com/ppetzold/nestjs-paginate/llms.txt Sets application-wide default pagination settings. This function should be called once during application bootstrap before any paginated routes are accessed. ```APIDOC ## `updateGlobalConfig` — Set application-wide pagination defaults Overrides the global defaults (`defaultLimit`, `defaultMaxLimit`, `defaultOrigin`) applied to every paginated route that does not supply its own values. Should be called once during application bootstrap before any paginated routes are hit. ```typescript // main.ts import { NestFactory } from '@nestjs/core' import { AppModule } from './app.module' import { updateGlobalConfig } from 'nestjs-paginate' async function bootstrap() { updateGlobalConfig({ defaultLimit: 25, // items per page when ?limit is omitted defaultMaxLimit: 200, // hard cap; query params above this are clamped defaultOrigin: 'https://api.example.com', // prefix for absolute pagination links }) const app = await NestFactory.create(AppModule) await app.listen(3000) } bootstrap() ``` ``` -------------------------------- ### Implement Custom Paginated Swagger Docs Decorator Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md Create a custom decorator, CustomPaginatedSwaggerDocs, by combining default decorators like Page, Limit, Where, and a custom SortBy decorator. This allows for a fully customized Swagger documentation experience for paginated endpoints. ```typescript const CustomApiPaginationQuery = (paginationConfig: PaginateConfig) => { return applyDecorators( ...[ Page(), Limit(paginationConfig), Where(paginationConfig), CustomSortBy(paginationConfig), Search(paginationConfig), SearchBy(paginationConfig), Select(paginationConfig), ].filter((v): v is MethodDecorator => v !== undefined) ) } function CustomPaginatedSwaggerDocs>(dto: DTO, paginatedConfig: PaginateConfig) { return applyDecorators(ApiOkPaginatedResponse(dto, paginatedConfig), CustomApiPaginationQuery(paginatedConfig)) } ``` -------------------------------- ### Paginate with Relations Configuration Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md Configure pagination to include relations, enabling sorting and filtering on related entity columns. Specify relations in the config and whitelist columns for filtering. ```typescript const config: PaginateConfig = { relations: ['toys'], sortableColumns: ['id', 'name', 'toys.name'], filterableColumns: { 'toys.name': [FilterOperator.IN], } } const result = await paginate(query, catRepo, config) ``` -------------------------------- ### Set Application-Wide Pagination Defaults Source: https://context7.com/ppetzold/nestjs-paginate/llms.txt Use `updateGlobalConfig` during application bootstrap to set default values for limit, max limit, and origin for all paginated routes. This should be called once before any paginated routes are accessed. ```typescript // main.ts import { NestFactory } from '@nestjs/core' import { AppModule } from './app.module' import { updateGlobalConfig } from 'nestjs-paginate' async function bootstrap() { updateGlobalConfig({ defaultLimit: 25, // items per page when ?limit is omitted defaultMaxLimit: 200, // hard cap; query params above this are clamped defaultOrigin: 'https://api.example.com', // prefix for absolute pagination links }) const app = await NestFactory.create(AppModule) await app.listen(3000) } bootstrap() ``` -------------------------------- ### Disable Pagination with `paginate` Source: https://context7.com/ppetzold/nestjs-paginate/llms.txt To disable pagination entirely and retrieve all rows, set `maxLimit` to -1 in the `PaginateConfig` and include `?limit=-1` in the query parameters. The `result.meta.totalItems` will reflect the total number of rows available. ```typescript // --- Disable pagination entirely (return all rows) --- // Set maxLimit: -1 in config AND send ?limit=-1 in query // result.meta.totalItems reflects all rows, no pagination links ``` -------------------------------- ### Generate Swagger Docs with Default Decorators Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md Use @ApiOkPaginatedResponse for the response body and @ApiPaginationQuery for query parameters to generate Swagger documentation for paginated endpoints. The http status code for the response is 200. ```typescript @Get() @ApiOkPaginatedResponse( UserDto, USER_PAGINATION_CONFIG, ) @ApiPaginationQuery(USER_PAGINATION_CONFIG) async findAll( @Paginate() query: PaginateQuery, ): Promise> { } ``` -------------------------------- ### Counter-Only Mode with `paginate` Source: https://context7.com/ppetzold/nestjs-paginate/llms.txt Enables counter-only mode by sending `?limit=0` in the query parameters. This mode returns an empty `result.data` array but correctly populates `result.meta.totalItems` with the total count of available items. ```typescript // --- Counter-only mode (get total count, no data rows) --- // Send ?limit=0 — result.data is [] but result.meta.totalItems is correct ``` -------------------------------- ### Paginate with Nested Relations Configuration Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md Configure pagination to handle deeply nested relations for sorting, searching, and filtering. Specify the nested relation paths in the configuration object. ```typescript const config: PaginateConfig = { relations: { home: { pillows: true } }, sortableColumns: ['id', 'name', 'home.pillows.color'], searchableColumns: ['name', 'home.pillows.color'], filterableColumns: { 'home.pillows.color': [FilterOperator.EQ], } } const result = await paginate(query, catRepo, config) ``` -------------------------------- ### Paginate with Eager Loading Relations Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md Configure pagination to load eager relations defined in your entities. Set `loadEagerRelations` to true in the paginate config to utilize TypeORM's eager loading. ```typescript @Entity() export class CatEntity { // ... @OneToMany(() => CatToyEntity, (catToy) => catToy.cat, { eager: true, }) toys: CatToyEntity[] } const config: PaginateConfig = { loadEagerRelations: true, sortableColumns: ['id', 'name', 'toys.name'], filterableColumns: { 'toys.name': [FilterOperator.IN], }, } const result = await paginate(query, catRepo, config) ``` -------------------------------- ### Paginate with AND-mode Configuration and Max Values Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md Configure AND-mode for filtering to-many relationships and optionally set `maxAndValues` to control the number of correlated EXISTS subqueries per sub-column. The default is 20. ```typescript const config: PaginateConfig = { sortableColumns: ['id'], filterableColumns: { 'toys.name': [FilterOperator.EQ, FilterComparator.AND], }, maxAndValues: 10, // optional, default is 20 } ``` -------------------------------- ### Paginate with to-many Relationships - $any Quantifier Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md Filter parent entities based on conditions met by at least one related entity in a to-many relationship. The '$any' quantifier is the default. ```url GET /cats?filter.toys.name=$any:$eq:Ball ``` ```url GET /cats?filter.toys.name=$any:$ilike:red ``` -------------------------------- ### Paginate with to-many Relationships - $all Quantifier Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md Filter parent entities where all related entities in a to-many relationship satisfy a given condition. Use the '$all' quantifier for this purpose. ```url GET /cats?filter.toys.name=$all:$sw:Chew ``` -------------------------------- ### Paginate with to-many Relationships - $none Quantifier Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md Filter parent entities that have no related entities satisfying a specific condition, or have no related entities at all. Use the '$none' quantifier. ```url GET /cats?filter.toys.name=$none:$eq:Squeaky ``` -------------------------------- ### Configure JSONB Column Filtering with Relations Source: https://github.com/ppetzold/nestjs-paginate/blob/master/README.md Configure the `PaginateConfig` to enable filtering on JSONB columns within relations. Specify the relation path and the JSONB field to be filtered. ```typescript const config: PaginateConfig = { relations: ['settings'], filterableColumns: { 'settings.theme': [FilterOperator.EQ, FilterOperator.IN], }, } ```