Gathering detailed insights and metrics for nestjs-paginate
Gathering detailed insights and metrics for nestjs-paginate
Gathering detailed insights and metrics for nestjs-paginate
Gathering detailed insights and metrics for 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)
npm install nestjs-paginate
Typescript
Module System
Node Version
NPM Version
68.6
Supply Chain
83.5
Quality
89.1
Maintenance
100
Vulnerability
98.6
License
TypeScript (100%)
Love this project? Help keep it running — sponsor us today! 🚀
Total Downloads
2,169,974
Last Day
875
Last Week
22,496
Last Month
96,893
Last Year
1,318,448
MIT License
487 Stars
886 Commits
104 Forks
8 Watchers
11 Branches
38 Contributors
Updated on Feb 18, 2025
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
Cumulative downloads
Total Downloads
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
1
5
25
Pagination and filtering helper method for TypeORM repositories or query builders using Nest.js framework.
$eq
, $not
, $null
, $in
, $gt
, $gte
, $lt
, $lte
, $btw
, $ilike
, $sw
, $contains
)npm install nestjs-paginate
The following code exposes a route that can be utilized like so:
1http://localhost:3000/cats?limit=5&page=2&sortBy=color:DESC&search=i&filter.age=$gte:3&select=id,name,color,age
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}
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}
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}
You can paginate custom queries by passing on the query builder:
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)
Similar as with repositories, you can utilize relations
as a simplified left-join form:
1http://localhost:3000/cats?filter.toys.name=$in:Mouse,String
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}
Similar as with relations, you can specify nested relations for sorting, filtering and searching:
1http://localhost:3000/cats?filter.home.pillows.color=pink
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)
Eager loading should work with TypeORM's eager property out of the box:
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)
Filter operators must be whitelisted per column in PaginateConfig
.
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
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 are filters that can be applied to a single column with a comparator.
?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 ...
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 }
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
Reason
6 existing vulnerabilities detected
Details
Reason
dependency not pinned by hash detected -- score normalized to 3
Details
Reason
detected GitHub workflow tokens with excessive permissions
Details
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
project is not fuzzed
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Score
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 More