Installations
npm install @trmaphi/nestjs-typeorm-paginate
Developer Guide
Typescript
No
Module System
CommonJS
Node Version
14.17.4
NPM Version
6.14.14
Releases
Unable to fetch releases
Contributors
Unable to fetch Contributors
Languages
TypeScript (98.27%)
JavaScript (1.73%)
Love this project? Help keep it running — sponsor us today! 🚀
Developer
nestjsx
Download Statistics
Total Downloads
1,073
Last Day
1
Last Week
3
Last Month
21
Last Year
103
GitHub Statistics
MIT License
856 Stars
763 Commits
129 Forks
9 Watchers
8 Branches
30 Contributors
Updated on Feb 14, 2025
Bundle Size
4.85 kB
Minified
1.61 kB
Minified + Gzipped
Package Meta Information
Latest Version
3.1.6
Package Id
@trmaphi/nestjs-typeorm-paginate@3.1.6
Unpacked Size
33.41 kB
Size
9.99 kB
File Count
23
NPM Version
6.14.14
Node Version
14.17.4
Total Downloads
Cumulative downloads
Total Downloads
1,073
Last Day
0%
1
Compared to previous day
Last Week
-25%
3
Compared to previous week
Last Month
200%
21
Compared to previous month
Last Year
-25.4%
103
Compared to previous year
Daily Downloads
Weekly Downloads
Monthly Downloads
Yearly Downloads
Dependencies
1
Peer Dependencies
2
Nestjs Typeorm paginate
Please vote on your prefered method of querying! Take+Skip vs Limit+Offset
Pagination helper method for TypeORM repositories or queryBuilders with strict typings
Install
1$ yarn add nestjs-typeorm-paginate
or
1$ npm i nestjs-typeorm-paginate
Usage
Service
Repository
1import { Injectable } from '@nestjs/common'; 2import { Repository } from 'typeorm'; 3import { InjectRepository } from '@nestjs/typeorm'; 4import { CatEntity } from './entities'; 5import { 6 paginate, 7 Pagination, 8 IPaginationOptions, 9} from 'nestjs-typeorm-paginate'; 10 11@Injectable() 12export class CatService { 13 constructor( 14 @InjectRepository(CatEntity) 15 private readonly repository: Repository<CatEntity>, 16 ) {} 17 18 async paginate(options: IPaginationOptions): Promise<Pagination<CatEntity>> { 19 return paginate<CatEntity>(this.repository, options); 20 } 21}
QueryBuilder
1import { Injectable } from '@nestjs/common'; 2import { Repository } from 'typeorm'; 3import { InjectRepository } from '@nestjs/typeorm'; 4import { CatEntity } from './entities'; 5import { 6 paginate, 7 Pagination, 8 IPaginationOptions, 9} from 'nestjs-typeorm-paginate'; 10 11@Injectable() 12export class CatService { 13 constructor( 14 @InjectRepository(CatEntity) 15 private readonly repository: Repository<CatEntity>, 16 ) {} 17 18 async paginate(options: IPaginationOptions): Promise<Pagination<CatEntity>> { 19 const queryBuilder = this.repository.createQueryBuilder('c'); 20 queryBuilder.orderBy('c.name', 'DESC'); // Or whatever you need to do 21 22 return paginate<CatEntity>(queryBuilder, options); 23 } 24}
Controller
1import { Controller, DefaultValuePipe, Get, ParseIntPipe, Query } from '@nestjs/common'; 2import { CatService } from './cat.service'; 3import { CatEntity } from './cat.entity'; 4import { Pagination } from 'nestjs-typeorm-paginate'; 5 6@Controller('cats') 7export class CatsController { 8 constructor(private readonly catService: CatService) {} 9 @Get('') 10 async index( 11 @Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number = 1, 12 @Query('limit', new DefaultValuePipe(10), ParseIntPipe) limit: number = 10, 13 ): Promise<Pagination<CatEntity>> { 14 limit = limit > 100 ? 100 : limit; 15 return this.catService.paginate({ 16 page, 17 limit, 18 route: 'http://cats.com/cats', 19 }); 20 } 21}
If you use
ParseIntPipe
on the query params (as in the example), don't forget to also addDefaultValuePipe
. See issue 517 for more info.
the
route
property of the paginate options can also be the short version of an absolute path , In this case, it would be/cats
instead ofhttp://cats.com/cats
Example Response
1{ 2 "items": [ 3 { 4 "lives": 9, 5 "type": "tabby", 6 "name": "Bobby" 7 }, 8 { 9 "lives": 2, 10 "type": "Ginger", 11 "name": "Garfield" 12 }, 13 { 14 "lives": 6, 15 "type": "Black", 16 "name": "Witch's mate" 17 }, 18 { 19 "lives": 7, 20 "type": "Purssian Grey", 21 "name": "Alisdaya" 22 }, 23 { 24 "lives": 1, 25 "type": "Alistair", 26 "name": "ali" 27 }, 28 ... 29 ], 30 "meta": { 31 "itemCount": 10, 32 "totalItems": 20, 33 "itemsPerPage": 10, 34 "totalPages": 5, 35 "currentPage": 2 36 }, 37 "links" : { 38 "first": "http://cats.com/cats?limit=10", 39 "previous": "http://cats.com/cats?page=1&limit=10", 40 "next": "http://cats.com/cats?page=3&limit=10", 41 "last": "http://cats.com/cats?page=5&limit=10" 42 } 43}
items
: An array of SomeEntity
meta.itemCount
: The length of items array (i.e., the amount of items on this page)
meta.totalItems
: The total amount of SomeEntity matching the filter conditions
meta.itemsPerPage
: The requested items per page (i.e., the limit
parameter)
meta.totalPages
: The total amount of pages (based on the limit
)
meta.currentPage
: The current page this paginator "points" to
links.first
: A URL for the first page to call | ""
(blank) if no route
is defined
links.previous
: A URL for the previous page to call | ""
(blank) if no previous to call
links.next
: A URL for the next page to call | ""
(blank) if no page to call
links.last
: A URL for the last page to call | ""
(blank) if no route
is defined
Do note that
links.first
may not have the 'page' query param defined
Find Parameters
1@Injectable() 2export class CatService { 3 constructor( 4 @InjectRepository(CatEntity) 5 private readonly repository: Repository<CatEntity>, 6 ) {} 7 8 async paginate(options: IPaginationOptions): Promise<Pagination<CatEntity>> { 9 return paginate<CatEntity>(this.repository, options, { 10 lives: 9, 11 }); 12 } 13}
Eager loading
Eager loading should work with typeorm's eager property out the box. Like so
1import { Entity, OneToMany } from 'typeorm'; 2 3@Entity() 4export class CatEntity { 5 @OneToMany(t => TigerKingEntity, tigerKing.cats, { 6 eager: true, 7 }) 8 tigerKings: TigerKingEntity[]; 9} 10 11// service 12class CatService { 13 constructor(private readonly repository: Repository<CatEntity>) {} 14 15 async paginate(page: number, limit: number): Promise<Pagination<CatEntity>> { 16 return paginate(this.repository, { page, limit }); 17 } 18}
QueryBuilder
However, when using the query builder you'll have to hydrate the entities yourself. Here is a crude example that I've used in the past. It's not great but this is partially what typeORM will do.
1const results = paginate(queryBuilder, { page, limit }); 2 3return new Pagination( 4 await Promise.all( 5 results.items.map(async (item: SomeEntity) => { 6 const hydrate = await this.someRepository.findByEntity(item); 7 item.hydrated = hydrate; 8 9 return item; 10 }), 11 ), 12 results.meta, 13 results.links, 14);
Raw queries
1const queryBuilder = this.repository 2 .createQueryBuilder<{ type: string; totalLives: string }>('c') 3 .select('c.type', 'type') 4 .addSelect('SUM(c.lives)', 'totalLives') 5 .groupBy('c.type') 6 .orderBy('c.type', 'DESC'); // Or whatever you need to do 7 8return paginateRaw(queryBuilder, options);
Raw and Entities
A similar approach is used for TypeORM's getRawAndEntities
Let's assume there's a joined table that matches each cat with its cat toys. And we want to bring how many toys each cat has.
1 2const queryBuilder = this.repository 3 .createQueryBuilder<{ type: string; totalLives: string }>('cat') 4 .leftJoinAndSelect('cat.toys', 'toys') 5 .addSelect('COUNT(toys)::INTEGER', 'toyCount') 6 .groupBy('cat.name');
This will allow us to get the paginated cats information with the additional raw query to build our actual response value. The return pagination object will be the same, but you're now able to handle or map the results and the raw objects as needed.
1const [pagination, rawResults] = await paginateRawAndEntities(query, options); 2pagination.items.map((item, index) => { 3 // we can do what we need with the items and raw results here 4 // change your items using rawResults.find(raw => raw.id === item.id) 5}); 6return pagination;
Note about joined tables and raw values
Since the values of the raw results will include all the joined table items as queried, you must make sure to handle the items as needed for your use case. Refer to TypeORM's getRawAndEntities implementation as needed.
The rawResults array will look something like this:
1[ 2 { // Bobby appears 3 times due to the joined query 3 "cat_lives": 9, 4 "cat_type": "tabby", 5 "cat_name": "Bobby", 6 "toyCount": 3 7 }, 8 { 9 "cat_lives": 9, 10 "cat_type": "tabby", 11 "cat_name": "Bobby", 12 "toyCount": 3 13 }, 14 { 15 "cat_lives": 9, 16 "cat_type": "tabby", 17 "cat_name": "Bobby", 18 "toyCount": 3 19 }, 20 { 21 "cat_lives": 2, 22 "cat_type": "Ginger", 23 "cat_name": "Garfield", 24 "toyCount": 1 25 }, 26 ... 27]
Custom meta data transformer
If you wanted to alter the meta data that is returned from the pagination object. Then use the metaTransformer
in the options like so
1 2class CustomPaginationMeta { 3 constructor( 4 public readonly count: number, 5 public readonly total: number, 6 ) {} 7} 8 9return paginate<MyEntity, CustomPaginationMeta>(this.repository, { 10 page, 11 limit, 12 metaTransformer: (meta: IPaginationMeta): CustomPaginationMeta => new CustomPaginationMeta( 13 meta.itemCount, 14 meta.totalItems, 15 ), 16 });
This will result in the above returning CustomPaginationMeta
in the meta
property instead of the default IPaginationMeta
.
Custom links query params labels
If you want to alter the limit
and/or page
labels in meta links, then use routingLabels
in the options like so
1 2return paginate<MyEntity>(this.repository, { 3 page, 4 limit, 5 routingLabels: { 6 limitLabel: 'page-size', // default: limit 7 pageLabel: 'current-page', //default: page 8 } 9 });
This will result links like http://example.com/something?current-page=1&page-size=3
.

No vulnerabilities found.
Reason
no dangerous workflow patterns detected
Reason
no binaries found in the repo
Reason
license file detected
Details
- Info: project has a license file: LICENSE:0
- Info: FSF or OSI recognized license: MIT License: LICENSE:0
Reason
Found 1/2 approved changesets -- score normalized to 5
Reason
8 existing vulnerabilities detected
Details
- Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg
- Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275
- Warn: Project is vulnerable to: GHSA-896r-f27r-55mw
- Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv
- Warn: Project is vulnerable to: GHSA-9wv6-86v2-598j
- Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6
- Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3
- Warn: Project is vulnerable to: GHSA-3h5v-q93c-6h6q
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
dependency not pinned by hash detected -- score normalized to 0
Details
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:28: update your workflow using https://app.stepsecurity.io/secureworkflow/nestjsx/nestjs-typeorm-paginate/main.yml/master?enable=pin
- Warn: third-party GitHubAction not pinned by hash: .github/workflows/main.yml:54: update your workflow using https://app.stepsecurity.io/secureworkflow/nestjsx/nestjs-typeorm-paginate/main.yml/master?enable=pin
- Warn: third-party GitHubAction not pinned by hash: .github/workflows/main.yml:65: update your workflow using https://app.stepsecurity.io/secureworkflow/nestjsx/nestjs-typeorm-paginate/main.yml/master?enable=pin
- Info: 0 out of 1 GitHub-owned GitHubAction dependencies pinned
- Info: 0 out of 2 third-party GitHubAction 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
branch protection not enabled on development/release branches
Details
- Warn: branch protection not enabled for branch 'master'
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
- Warn: 0 commits out of 29 are checked with a SAST tool
Score
3.2
/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 More