Installations
npm install nestjs-paginate
Developer Guide
Typescript
No
Module System
CommonJS
Node Version
20.18.0
NPM Version
10.8.2
Score
68.6
Supply Chain
83.5
Quality
89.1
Maintenance
100
Vulnerability
98.6
License
Releases
Contributors
Languages
TypeScript (100%)
Love this project? Help keep it running — sponsor us today! 🚀
Developer
ppetzold
Download Statistics
Total Downloads
2,169,974
Last Day
875
Last Week
22,496
Last Month
96,893
Last Year
1,318,448
GitHub Statistics
MIT License
487 Stars
886 Commits
104 Forks
8 Watchers
11 Branches
38 Contributors
Updated on Feb 18, 2025
Package Meta Information
Latest Version
11.0.1
Package Id
nestjs-paginate@11.0.1
Unpacked Size
594.01 kB
Size
119.61 kB
File Count
67
NPM Version
10.8.2
Node Version
20.18.0
Published on
Feb 18, 2025
Total Downloads
Cumulative downloads
Total Downloads
2,169,974
Last Day
19.4%
875
Compared to previous day
Last Week
-21.3%
22,496
Compared to previous week
Last Month
43%
96,893
Compared to previous month
Last Year
115.9%
1,318,448
Compared to previous year
Daily Downloads
Weekly Downloads
Monthly Downloads
Yearly Downloads
Dependencies
1
Peer Dependencies
5
Dev Dependencies
25
Nest.js Paginate
Pagination and filtering helper method for TypeORM repositories or query builders using Nest.js framework.
- Pagination conforms to JSON:API
- Sort by multiple columns
- Search across columns
- Select columns
- Filter using operators (
$eq
,$not
,$null
,$in
,$gt
,$gte
,$lt
,$lte
,$btw
,$ilike
,$sw
,$contains
) - Include relations and nested relations
- Virtual column support
Installation
npm install nestjs-paginate
Usage
Example
The following code exposes a route that can be utilized like so:
Endpoint
1http://localhost:3000/cats?limit=5&page=2&sortBy=color:DESC&search=i&filter.age=$gte:3&select=id,name,color,age
Result
1{ 2 "data": [ 3 { 4 "id": 4, 5 "name": "George", 6 "color": "white", 7 "age": 3 8 }, 9 { 10 "id": 5, 11 "name": "Leche", 12 "color": "white", 13 "age": 6 14 }, 15 { 16 "id": 2, 17 "name": "Garfield", 18 "color": "ginger", 19 "age": 4 20 }, 21 { 22 "id": 1, 23 "name": "Milo", 24 "color": "brown", 25 "age": 5 26 }, 27 { 28 "id": 3, 29 "name": "Kitty", 30 "color": "black", 31 "age": 3 32 } 33 ], 34 "meta": { 35 "itemsPerPage": 5, 36 "totalItems": 12, 37 "currentPage": 2, 38 "totalPages": 3, 39 "sortBy": [["color", "DESC"]], 40 "search": "i", 41 "filter": { 42 "age": "$gte:3" 43 } 44 }, 45 "links": { 46 "first": "http://localhost:3000/cats?limit=5&page=1&sortBy=color:DESC&search=i&filter.age=$gte:3", 47 "previous": "http://localhost:3000/cats?limit=5&page=1&sortBy=color:DESC&search=i&filter.age=$gte:3", 48 "current": "http://localhost:3000/cats?limit=5&page=2&sortBy=color:DESC&search=i&filter.age=$gte:3", 49 "next": "http://localhost:3000/cats?limit=5&page=3&sortBy=color:DESC&search=i&filter.age=$gte:3", 50 "last": "http://localhost:3000/cats?limit=5&page=3&sortBy=color:DESC&search=i&filter.age=$gte:3" 51 } 52}
Code
1import { Controller, Injectable, Get } from '@nestjs/common' 2import { InjectRepository } from '@nestjs/typeorm' 3import { FilterOperator, FilterSuffix, Paginate, PaginateQuery, paginate, Paginated } from 'nestjs-paginate' 4import { Repository, Entity, PrimaryGeneratedColumn, Column } from 'typeorm' 5 6@Entity() 7export class CatEntity { 8 @PrimaryGeneratedColumn() 9 id: number 10 11 @Column('text') 12 name: string 13 14 @Column('text') 15 color: string 16 17 @Column('int') 18 age: number 19 20 @Column({ nullable: true }) 21 lastVetVisit: Date | null 22 23 @CreateDateColumn() 24 createdAt: string 25} 26 27@Injectable() 28export class CatsService { 29 constructor( 30 @InjectRepository(CatEntity) 31 private readonly catsRepository: Repository<CatEntity> 32 ) {} 33 34 public findAll(query: PaginateQuery): Promise<Paginated<CatEntity>> { 35 return paginate(query, this.catsRepository, { 36 sortableColumns: ['id', 'name', 'color', 'age'], 37 nullSort: 'last', 38 defaultSortBy: [['id', 'DESC']], 39 searchableColumns: ['name', 'color', 'age'], 40 select: ['id', 'name', 'color', 'age', 'lastVetVisit'], 41 filterableColumns: { 42 name: [FilterOperator.EQ, FilterSuffix.NOT], 43 age: true, 44 }, 45 }) 46 } 47} 48 49@Controller('cats') 50export class CatsController { 51 constructor(private readonly catsService: CatsService) {} 52 53 @Get() 54 public findAll(@Paginate() query: PaginateQuery): Promise<Paginated<CatEntity>> { 55 return this.catsService.findAll(query) 56 } 57}
Config
1const paginateConfig: PaginateConfig<CatEntity> { 2 /** 3 * Required: true (must have a minimum of one column) 4 * Type: (keyof CatEntity)[] 5 * Description: These are the columns that are valid to be sorted by. 6 */ 7 sortableColumns: ['id', 'name', 'color'], 8 9 /** 10 * Required: false 11 * Type: 'first' | 'last' 12 * Description: Define whether to put null values at the beginning 13 * or end of the result set. 14 */ 15 nullSort: 'last', 16 17 /** 18 * Required: false 19 * Type: [keyof CatEntity, 'ASC' | 'DESC'][] 20 * Default: [[sortableColumns[0], 'ASC]] 21 * Description: The order to display the sorted entities. 22 */ 23 defaultSortBy: [['name', 'DESC']], 24 25 /** 26 * Required: false 27 * Type: (keyof CatEntity)[] 28 * Description: These columns will be searched through when using the search query 29 * param. Limit search scope further by using `searchBy` query param. 30 */ 31 searchableColumns: ['name', 'color'], 32 33 /** 34 * Required: false 35 * Type: (keyof CatEntity)[] 36 * Default: None 37 * Description: TypeORM partial selection. Limit selection further by using `select` query param. 38 * https://typeorm.io/select-query-builder#partial-selection 39 * Note: You must include the primary key in the selection. 40 */ 41 select: ['id', 'name', 'color'], 42 43 /** 44 * Required: false 45 * Type: number 46 * Default: 100 47 * Description: The maximum amount of entities to return per page. 48 * Set it to -1, in conjunction with limit=-1 on query param, to disable pagination. 49 */ 50 maxLimit: 20, 51 52 /** 53 * Required: false 54 * Type: number 55 * Default: 20 56 */ 57 defaultLimit: 50, 58 59 /** 60 * Required: false 61 * Type: TypeORM find options 62 * Default: None 63 * https://typeorm.io/#/find-optionsfind-options.md 64 */ 65 where: { color: 'ginger' }, 66 67 /** 68 * Required: false 69 * Type: { [key in CatEntity]?: FilterOperator[] } - Operators based on TypeORM find operators 70 * Default: None 71 * https://typeorm.io/#/find-options/advanced-options 72 */ 73 filterableColumns: { age: [FilterOperator.EQ, FilterOperator.IN] }, 74 75 /** 76 * Required: false 77 * Type: RelationColumn<CatEntity> 78 * Description: Indicates what relations of entity should be loaded. 79 */ 80 relations: [], 81 82 /** 83 * Required: false 84 * Type: boolean 85 * Default: false 86 * Description: Load eager relations using TypeORM's eager property. 87 * Only works if `relations` is not defined. 88 */ 89 loadEagerRelations: true, 90 91 /** 92 * Required: false 93 * Type: boolean 94 * Description: Disables the global condition of "non-deleted" for the entity with delete date columns. 95 * https://typeorm.io/select-query-builder#querying-deleted-rows 96 */ 97 withDeleted: false, 98 99 /** 100 * Required: false 101 * Type: string 102 * Description: Allow user to choose between limit/offset and take/skip. 103 * Default: PaginationType.TAKE_AND_SKIP 104 * 105 * However, using limit/offset can cause problems with relations. 106 */ 107 paginationType: PaginationType.LIMIT_AND_OFFSET, 108 109 /** 110 * Required: false 111 * Type: boolean 112 * Default: false 113 * Description: Generate relative paths in the resource links. 114 */ 115 relativePath: true, 116 117 /** 118 * Required: false 119 * Type: string 120 * Description: Overrides the origin of absolute resource links if set. 121 */ 122 origin: 'http://cats.example', 123 124 /** 125 * Required: false 126 * Type: boolean 127 * Default: false 128 * Description: Prevent `searchBy` query param from limiting search scope further. Search will depend upon `searchableColumns` config option only 129 */ 130 ignoreSearchByInQueryParam: true, 131 132 /** 133 * Required: false 134 * Type: boolean 135 * Default: false 136 * Description: Prevent `select` query param from limiting selection further. Partial selection will depend upon `select` config option only 137 */ 138 ignoreSelectInQueryParam: true, 139 140 /** 141 * Required: false 142 * Type: boolean 143 * Default: false 144 * Description: Enable multi-word search behavior. When true, each word in the search query 145 * will be treated as a separate search term, allowing for more flexible matching. 146 */ 147 multiWordSearch: false, 148}
Usage with Query Builder
You can paginate custom queries by passing on the query builder:
Example
1const queryBuilder = repo 2 .createQueryBuilder('cats') 3 .leftJoinAndSelect('cats.owner', 'owner') 4 .where('cats.owner = :ownerId', { ownerId }) 5 6const result = await paginate<CatEntity>(query, queryBuilder, config)
Usage with Relations
Similar as with repositories, you can utilize relations
as a simplified left-join form:
Example
Endpoint
1http://localhost:3000/cats?filter.toys.name=$in:Mouse,String
Code
1const config: PaginateConfig<CatEntity> = { 2 relations: ['toys'], 3 sortableColumns: ['id', 'name', 'toys.name'], 4 filterableColumns: { 5 'toys.name': [FilterOperator.IN], 6 }, 7} 8 9const result = await paginate<CatEntity>(query, catRepo, config)
Note: Embedded columns on relations have to be wrapped with brackets:
1const config: PaginateConfig<CatEntity> = { 2 sortableColumns: ['id', 'name', 'toys.(size.height)', 'toys.(size.width)'], 3 searchableColumns: ['name'], 4 relations: ['toys'], 5}
Usage with Nested Relations
Similar as with relations, you can specify nested relations for sorting, filtering and searching:
Example
Endpoint
1http://localhost:3000/cats?filter.home.pillows.color=pink
Code
1const config: PaginateConfig<CatEntity> = { 2 relations: { home: { pillows: true } }, 3 sortableColumns: ['id', 'name', 'home.pillows.color'], 4 searchableColumns: ['name', 'home.pillows.color'], 5 filterableColumns: { 6 'home.pillows.color': [FilterOperator.EQ], 7 }, 8} 9 10const result = await paginate<CatEntity>(query, catRepo, config)
Usage with Eager Loading
Eager loading should work with TypeORM's eager property out of the box:
Example
Code
1@Entity() 2export class CatEntity { 3 // ... 4 5 @OneToMany(() => CatToyEntity, (catToy) => catToy.cat, { 6 eager: true, 7 }) 8 toys: CatToyEntity[] 9} 10 11const config: PaginateConfig<CatEntity> = { 12 loadEagerRelations: true, 13 sortableColumns: ['id', 'name', 'toys.name'], 14 filterableColumns: { 15 'toys.name': [FilterOperator.IN], 16 }, 17} 18 19const result = await paginate<CatEntity>(query, catRepo, config)
Filters
Filter operators must be whitelisted per column in PaginateConfig
.
Examples
Code
1const config: PaginateConfig<CatEntity> = { 2 // ... 3 filterableColumns: { 4 // Enable individual operators on a column 5 id: [FilterOperator.EQ, FilterSuffix.NOT], 6 7 // Enable all operators on a column 8 age: true, 9 }, 10}
?filter.name=$eq:Milo
is equivalent with ?filter.name=Milo
?filter.age=$btw:4,6
where column age
is between 4
and 6
?filter.id=$not:$in:2,5,7
where column id
is not 2
, 5
or 7
?filter.summary=$not:$ilike:term
where column summary
does not contain term
?filter.summary=$sw:term
where column summary
starts with term
?filter.seenAt=$null
where column seenAt
is NULL
?filter.seenAt=$not:$null
where column seenAt
is not NULL
?filter.createdAt=$btw:2022-02-02,2022-02-10
where column createdAt
is between the dates 2022-02-02
and 2022-02-10
?filter.createdAt=$lt:2022-12-20T10:00:00.000Z
where column createdAt
is before iso date 2022-12-20T10:00:00.000Z
?filter.roles=$contains:moderator
where column roles
is an array and contains the value moderator
?filter.roles=$contains:moderator,admin
where column roles
is an array and contains the values moderator
and admin
Jsonb Filters
You can filter on jsonb columns by using the dot notation. Json columns is limited to $eq
operators only.
?filter.metadata.enabled=$eq:true
where column metadata
is jsonb and contains an object with the key enabled
.
Multi Filters
Multi filters are filters that can be applied to a single column with a comparator.
Examples
?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
?filter.roles=$contains:moderator&filter.roles=$or:$contains:admin
where column roles
is an array and contains moderator
or admin
?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
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
Note: The first comparator on the the first filter is ignored because the filters are grouped by the column name and chained with an $and
to other filters.
...&filter.id=5&filter.id=$or:7&filter.name=Milo&...
is resolved to:
WHERE ... AND (id = 5 OR id = 7) AND name = 'Milo' AND ...
Swagger
You can use two default decorators @ApiOkResponsePaginated and @ApiPagination to generate swagger documentation for your endpoints
@ApiOkPaginatedResponse
is for response body, return http status is 200
@ApiPaginationQuery
is for query params
1 @Get() 2 @ApiOkPaginatedResponse( 3 UserDto, 4 USER_PAGINATION_CONFIG, 5 ) 6 @ApiPaginationQuery(USER_PAGINATION_CONFIG) 7 async findAll( 8 @Paginate() 9 query: PaginateQuery, 10 ): Promise<Paginated<UserEntity>> { 11 12 }
There is also some syntax sugar for this, and you can use only one decorator @PaginatedSwaggerDocs
for both response body and query params
1 @Get() 2 @PaginatedSwaggerDocs(UserDto, USER_PAGINATION_CONFIG) 3 async findAll( 4 @Paginate() 5 query: PaginateQuery, 6 ): Promise<Paginated<UserEntity>> { 7 8 }
Troubleshooting
The package does not report error reasons in the response bodies. They are instead
reported as debug
level logging.
Common errors include missing sortableColumns
or filterableColumns
(the latter only affects filtering).

No vulnerabilities found.
Reason
9 commit(s) and 7 issue activity found in the last 90 days -- score normalized to 10
Reason
no dangerous workflow patterns detected
Reason
no binaries found in the repo
Reason
all changesets reviewed
Reason
license file detected
Details
- Info: project has a license file: LICENSE:0
- Info: FSF or OSI recognized license: MIT License: LICENSE:0
Reason
6 existing vulnerabilities detected
Details
- Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275
- Warn: Project is vulnerable to: GHSA-rc47-6667-2j5j
- Warn: Project is vulnerable to: GHSA-2p57-rm9w-gvfp
- Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv
- Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw
- Warn: Project is vulnerable to: GHSA-f5x3-32g6-xq36
Reason
dependency not pinned by hash detected -- score normalized to 3
Details
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:44: update your workflow using https://app.stepsecurity.io/secureworkflow/ppetzold/nestjs-paginate/main.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:46: update your workflow using https://app.stepsecurity.io/secureworkflow/ppetzold/nestjs-paginate/main.yml/master?enable=pin
- Warn: third-party GitHubAction not pinned by hash: .github/workflows/main.yml:62: update your workflow using https://app.stepsecurity.io/secureworkflow/ppetzold/nestjs-paginate/main.yml/master?enable=pin
- Warn: downloadThenRun not pinned by hash: .github/workflows/main.yml:59
- Info: 0 out of 2 GitHub-owned GitHubAction dependencies pinned
- Info: 0 out of 1 third-party GitHubAction dependencies pinned
- Info: 1 out of 1 npmCommand dependencies pinned
- Info: 0 out of 1 downloadThenRun dependencies pinned
Reason
detected GitHub workflow tokens with excessive permissions
Details
- Warn: no topLevel permission defined: .github/workflows/main.yml:1
- Info: no jobLevel write permissions found
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
- Warn: no security policy file detected
- Warn: no security file to analyze
- Warn: no security file to analyze
- Warn: no security file to analyze
Reason
project is not fuzzed
Details
- Warn: no fuzzer integrations found
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
- Warn: 0 commits out of 30 are checked with a SAST tool
Score
5.4
/10
Last Scanned on 2025-02-10
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn MoreOther packages similar to nestjs-paginate
nestjs-typeorm-paginate
A simple pagination function to build a pagination object with types
nestjs-paginate-relations-filter-middleware
An enhanced middleware for capturing unrelated filters for nestjs-paginate lib
@senaiplay/nestjs-sequelize-paginate
🏳🌈 Pagination helper method for Sequelize models.
@pwease/nestjs-paginate
Pagination and filtering helper method for TypeORM repostiories or query builders using Nest.js framework. (Fork of https://www.npmjs.com/package/nestjs-paginate)