diff --git a/README.md b/README.md index 84b8e0f..3f5b141 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ interface IPaginateParams { currentPage: number, isFromStart?: boolean, isLengthAware?: boolean, + disabled?: boolean, } interface IWithPagination { @@ -62,6 +63,14 @@ const result = await knex('persons') // result.data - will hold persons data // result.pagination - will hold pagination object ``` +## paginate options + +| Key | type | Value | +|-----------------------------|---------|----------------------------------------------------------------------| +| perPage | number | Items per page. | +| currentPage | number | The current page | +| isFromStart / isLengthAware | boolean | Indicates if the returned list should get all pages till currentPage | +| disabled | boolean | Disables the pagination functionality, returns all relevant rows | ## `pagination` object @@ -72,12 +81,14 @@ const result = await knex('persons') | from | Counting ID of the first item of the current page. | | to | Counting ID of the last item of the current page. | -If `isLengthAware == true` or `currentPage == 1` or `isFromStart == true` pagination object will contain `total` & `lastPage`: +If `isLengthAware == true` or `currentPage == 1` or `isFromStart == true` pagination object will contain additional fields: -| Key | Value | -|----------|-------------------------------------------| -| total | Total items that the full query contains. | -| lastPage | Last page number. | +| Key | Value | +|----------|-----------------------------------------------------| +| total | Total items that the full query contains. | +| lastPage | Last page number. | +| nextPage | The next page or `null` when at the last page. | +| prevPage | The previous page or `null` when at the first page. | This lib got inspiration from [`knex-paginator`](https://github.com/cannblw/knex-paginator). diff --git a/__tests-tsd__/types.test-d.ts b/__tests-tsd__/types.test-d.ts index e104a19..54404cf 100644 --- a/__tests-tsd__/types.test-d.ts +++ b/__tests-tsd__/types.test-d.ts @@ -88,4 +88,15 @@ interface User { }) ).pagination ); + + expectType( + ( + await db('users').select('*').paginate({ + perPage: 10, + currentPage: 1, + isFromStart: true, + disabled: true, + }) + ).pagination + ); })(); diff --git a/__tests__/index.spec.js b/__tests__/index.spec.js index 9d124be..38c75ec 100644 --- a/__tests__/index.spec.js +++ b/__tests__/index.spec.js @@ -96,7 +96,7 @@ describe('paginate', () => { }); }); - ['isFromStart', 'isLengthAware'].forEach((param) => { + ['isFromStart', 'isLengthAware', 'disabled'].forEach((param) => { it(`should throw if ${param} is not a boolean`, () => { expect(() => db('persons').paginate({ [param]: 'x' })).toThrowError( `Paginate error: ${param} must be a boolean.` @@ -341,5 +341,37 @@ describe('paginate', () => { }); }); }); + + describe('disabled mode', () => { + it('should fetch all data when disabled', async () => { + const perPage = 2; + const currentPage = 2; + + const result = await db('persons').paginate({ perPage, currentPage, disabled: true }); + + expect(result.data).toHaveLength(total); + expect(result.pagination).toMatchObject({ + current_page: 1, + per_page: total, + from: 0, + to: total, + }); + }); + + it('should not fetch totals when disabled', async () => { + const perPage = 2; + const currentPage = 1; + + const result = await db('persons').paginate({ + perPage, + currentPage, + isFromStart: true, + disabled: true, + }); + + expect(result.data).toHaveLength(total); + expect(result.pagination).not.toHaveProperty('total', 'last_page'); + }); + }); }); }); diff --git a/lib/index.js b/lib/index.js index 0c89410..962c879 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,22 +1,19 @@ const Knex = require('knex'); +const { assertNumber, assertBoolean } = require('./utils'); module.exports.attachPaginate = function attachPaginate() { - function paginate({ perPage = 10, currentPage = 1, isFromStart = false, isLengthAware = false }) { - if (isNaN(perPage)) { - throw new Error('Paginate error: perPage must be a number.'); - } - - if (isNaN(currentPage)) { - throw new Error('Paginate error: currentPage must be a number.'); - } - - if (typeof isFromStart !== 'boolean') { - throw new Error('Paginate error: isFromStart must be a boolean.'); - } - - if (typeof isLengthAware !== 'boolean') { - throw new Error('Paginate error: isLengthAware must be a boolean.'); - } + function paginate({ + perPage = 10, + currentPage = 1, + isFromStart = false, + isLengthAware = false, + disabled = false, + }) { + assertNumber('perPage', perPage); + assertNumber('currentPage', currentPage); + assertBoolean('isFromStart', isFromStart); + assertBoolean('isLengthAware', isLengthAware); + assertBoolean('disabled', disabled); if (currentPage < 1) { currentPage = 1; @@ -29,8 +26,8 @@ module.exports.attachPaginate = function attachPaginate() { let pagination = { perPage, - currentPage, - from: offset, + currentPage: disabled ? 1 : currentPage, + from: disabled ? 0 : offset, to: undefined, // will be assigned when we fetch the data }; @@ -41,14 +38,16 @@ module.exports.attachPaginate = function attachPaginate() { const originalQuery = shouldFetchTotals ? this.clone() : null; - // This will paginate the data itself - this.offset(offset).limit(limit); + if (!disabled) { + // This will paginate the data itself + this.offset(offset).limit(limit); + } return this.client.transaction(async (trx) => { const data = await this.transacting(trx); pagination.to = offset + data.length; - if (shouldFetchTotals) { + if (shouldFetchTotals && !disabled) { const countResult = await new this.constructor(this.client) .count('* as total') .from(originalQuery.clear('offset').clearOrder().as('count__query__')) @@ -65,6 +64,8 @@ module.exports.attachPaginate = function attachPaginate() { prevPage: currentPage > 1 ? currentPage - 1 : null, nextPage: currentPage < lastPage ? currentPage + 1 : null, }; + } else if (disabled) { + pagination.perPage = pagination.to = data.length; } return { data, pagination: postProcessResponse(pagination) }; diff --git a/lib/utils.js b/lib/utils.js new file mode 100644 index 0000000..ce8d6cc --- /dev/null +++ b/lib/utils.js @@ -0,0 +1,16 @@ +function assertNumber(paramName, value) { + if (isNaN(value)) { + throw new Error(`Paginate error: ${paramName} must be a number.`); + } +} + +function assertBoolean(paramName, value) { + if (typeof value !== 'boolean') { + throw new Error(`Paginate error: ${paramName} must be a boolean.`); + } +} + +module.exports = { + assertNumber, + assertBoolean, +}; diff --git a/types.d.ts b/types.d.ts index 22f0e6c..be6d66c 100644 --- a/types.d.ts +++ b/types.d.ts @@ -5,6 +5,7 @@ interface IPaginateParams { currentPage: number; isFromStart?: boolean; isLengthAware?: boolean; + disabled?: boolean; } interface IWithPagination { @@ -12,10 +13,11 @@ interface IWithPagination { pagination: IPagination; } -type IPagination = TParams extends +type IPagination = TParams extends ( | { currentPage: 1 } | { isFromStart: true } | { isLengthAware: true } +) & { disabled?: false } ? ILengthAwarePagination : IBasePagination;