{"id":15064660,"url":"https://github.com/ppetzold/nestjs-paginate","last_synced_at":"2026-04-07T11:02:12.235Z","repository":{"id":36956640,"uuid":"275108996","full_name":"ppetzold/nestjs-paginate","owner":"ppetzold","description":"Pagination and filtering helper method for TypeORM repositories or query builders using Nest.js framework :book::paperclip:","archived":false,"fork":false,"pushed_at":"2026-04-01T14:49:21.000Z","size":3354,"stargazers_count":571,"open_issues_count":81,"forks_count":121,"subscribers_count":7,"default_branch":"master","last_synced_at":"2026-04-01T16:30:41.632Z","etag":null,"topics":["filtering","nestjs","pagination"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ppetzold.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-06-26T08:29:49.000Z","updated_at":"2026-03-29T13:39:39.000Z","dependencies_parsed_at":"2026-01-30T23:02:14.930Z","dependency_job_id":null,"html_url":"https://github.com/ppetzold/nestjs-paginate","commit_stats":{"total_commits":882,"total_committers":38,"mean_commits":"23.210526315789473","dds":0.2290249433106576,"last_synced_commit":"ca36807f1f8ebb79f62b29269a5361fdc70d2ec5"},"previous_names":[],"tags_count":132,"template":false,"template_full_name":null,"purl":"pkg:github/ppetzold/nestjs-paginate","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ppetzold%2Fnestjs-paginate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ppetzold%2Fnestjs-paginate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ppetzold%2Fnestjs-paginate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ppetzold%2Fnestjs-paginate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ppetzold","download_url":"https://codeload.github.com/ppetzold/nestjs-paginate/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ppetzold%2Fnestjs-paginate/sbom","scorecard":{"id":742721,"data":{"date":"2025-08-18","repo":{"name":"github.com/ppetzold/nestjs-paginate","commit":"2d71292fb1e50a5235a0d162efe6726733b1060f"},"scorecard":{"version":"v5.2.1-41-g40576783","commit":"40576783fda6698350fcbbeaea760ff827433034"},"score":4.9,"checks":[{"name":"Maintained","score":10,"reason":"4 commit(s) and 10 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#packaging"}},{"name":"Code-Review","score":9,"reason":"Found 15/16 approved changesets -- score normalized to 9","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":0,"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"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":3,"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"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#binary-artifacts"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#license"}},{"name":"Security-Policy","score":0,"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"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#fuzzing"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#branch-protection"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#signed-releases"}},{"name":"SAST","score":0,"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"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"15 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-cj7v-w2c7-cp7c","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","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-44fp-w29j-9vj5","Warn: Project is vulnerable to: GHSA-4pg4-qvpc-4q3h","Warn: Project is vulnerable to: GHSA-g5hg-p3ph-g8qg","Warn: Project is vulnerable to: GHSA-fjgf-rc76-4x9p","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-95m3-7q98-8xr5","Warn: Project is vulnerable to: GHSA-f5x3-32g6-xq36","Warn: Project is vulnerable to: GHSA-8cj5-5rvv-wf4v"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-22T17:54:24.597Z","repository_id":36956640,"created_at":"2025-08-22T17:54:24.597Z","updated_at":"2025-08-22T17:54:24.597Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31509943,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-07T03:10:19.677Z","status":"ssl_error","status_checked_at":"2026-04-07T03:10:13.982Z","response_time":105,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["filtering","nestjs","pagination"],"created_at":"2024-09-25T00:23:46.012Z","updated_at":"2026-04-07T11:02:12.229Z","avatar_url":"https://github.com/ppetzold.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Nest.js Paginate\n\n![Main CI](https://github.com/ppetzold/nestjs-paginate/workflows/Main%20CI/badge.svg)\n[![npm](https://img.shields.io/npm/v/nestjs-paginate.svg)](https://www.npmjs.com/package/nestjs-paginate)\n[![downloads](https://img.shields.io/npm/dt/nestjs-paginate.svg)](https://www.npmjs.com/package/nestjs-paginate)\n[![codecov](https://codecov.io/gh/ppetzold/nestjs-paginate/branch/master/graph/badge.svg)](https://codecov.io/gh/ppetzold/nestjs-paginate)\n[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier)\n[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)\n![GitHub](https://img.shields.io/github/license/ppetzold/nestjs-paginate)\n\nPagination and filtering helper method for TypeORM repositories or query builders using [Nest.js](https://nestjs.com/) framework.\n\n- Pagination conforms to [JSON:API](https://jsonapi.org/)\n- Sort by multiple columns\n- Search across columns\n- Select columns\n- Filter using operators (`$eq`, `$not`, `$null`, `$in`, `$gt`, `$gte`, `$lt`, `$lte`, `$btw`, `$ilike`, `$sw`, `$contains`)\n- Include relations and nested relations\n- Virtual column support\n- Cursor-based pagination\n\n## Installation\n\n```\nnpm install nestjs-paginate\n```\n\n## Usage\n\n### Global configurations\n\nYou can configure the global settings for all paginated routes by updating the default global configuration\nusing below method. Ideally, you need to do it as soon as possible in your application main bootstrap method,\nas it affects all paginated routes, and swagger generation logic.\n\n```typescript\nimport { updateGlobalConfig } from 'nestjs-paginate'\n\nupdateGlobalConfig({\n  // this is default configuration\n  defaultOrigin: undefined,\n  defaultLimit: 20,\n  defaultMaxLimit: 100,\n});\n```\n\n\n\n### Example\n\nThe following code exposes a route that can be utilized like so:\n\n#### Endpoint\n\n```url\nhttp://localhost:3000/cats?limit=5\u0026page=2\u0026sortBy=color:DESC\u0026search=i\u0026filter.age=$gte:3\u0026select=id,name,color,age\u0026withDeleted=true\n```\n\n#### Result\n\n```json\n{\n  \"data\": [\n    {\n      \"id\": 4,\n      \"name\": \"George\",\n      \"color\": \"white\",\n      \"age\": 3\n    },\n    {\n      \"id\": 5,\n      \"name\": \"Leche\",\n      \"color\": \"white\",\n      \"age\": 6\n    },\n    {\n      \"id\": 2,\n      \"name\": \"Garfield\",\n      \"color\": \"ginger\",\n      \"age\": 4\n    },\n    {\n      \"id\": 1,\n      \"name\": \"Milo\",\n      \"color\": \"brown\",\n      \"age\": 5\n    },\n    {\n      \"id\": 3,\n      \"name\": \"Kitty\",\n      \"color\": \"black\",\n      \"age\": 3\n    }\n  ],\n  \"meta\": {\n    \"itemsPerPage\": 5,\n    \"totalItems\": 12,\n    \"currentPage\": 2,\n    \"totalPages\": 3,\n    \"sortBy\": [[\"color\", \"DESC\"]],\n    \"search\": \"i\",\n    \"filter\": {\n      \"age\": \"$gte:3\"\n    }\n  },\n  \"links\": {\n    \"first\": \"http://localhost:3000/cats?limit=5\u0026page=1\u0026sortBy=color:DESC\u0026search=i\u0026filter.age=$gte:3\",\n    \"previous\": \"http://localhost:3000/cats?limit=5\u0026page=1\u0026sortBy=color:DESC\u0026search=i\u0026filter.age=$gte:3\",\n    \"current\": \"http://localhost:3000/cats?limit=5\u0026page=2\u0026sortBy=color:DESC\u0026search=i\u0026filter.age=$gte:3\",\n    \"next\": \"http://localhost:3000/cats?limit=5\u0026page=3\u0026sortBy=color:DESC\u0026search=i\u0026filter.age=$gte:3\",\n    \"last\": \"http://localhost:3000/cats?limit=5\u0026page=3\u0026sortBy=color:DESC\u0026search=i\u0026filter.age=$gte:3\"\n  }\n}\n```\n\n### Example (Cursor-based Pagination)\n\nThe following code exposes a route using cursor-based pagination:\n\n#### Endpoint\n\n```url\nhttp://localhost:3000/cats?limit=5\u0026sortBy=lastVetVisit:ASC\u0026cursor=V998328469600000\n```\n\n#### Result\n\n```json\n{\n  \"data\": [\n    {\n      \"id\": 3,\n      \"name\": \"Shadow\",\n      \"lastVetVisit\": \"2022-12-21T10:00:00.000Z\"\n    },\n    {\n      \"id\": 4,\n      \"name\": \"Luna\",\n      \"lastVetVisit\": \"2022-12-22T10:00:00.000Z\"\n    },\n    {\n      \"id\": 5,\n      \"name\": \"Pepper\",\n      \"lastVetVisit\": \"2022-12-23T10:00:00.000Z\"\n    },\n    {\n      \"id\": 6,\n      \"name\": \"Simba\",\n      \"lastVetVisit\": \"2022-12-24T10:00:00.000Z\"\n    },\n    {\n      \"id\": 7,\n      \"name\": \"Tiger\",\n      \"lastVetVisit\": \"2022-12-25T10:00:00.000Z\"\n    }\n  ],\n  \"meta\": {\n    \"itemsPerPage\": 5,\n    \"cursor\": \"V998328469600000\"\n  },\n  \"links\": {\n    \"previous\": \"http://localhost:3000/cats?limit=5\u0026sortBy=lastVetVisit:DESC\u0026cursor=V001671616800000\",\n    \"current\": \"http://localhost:3000/cats?limit=5\u0026sortBy=lastVetVisit:ASC\u0026cursor=V998328469600000\",\n    \"next\": \"http://localhost:3000/cats?limit=5\u0026sortBy=lastVetVisit:ASC\u0026cursor=V998328037600000\"\n  }\n}\n```\n\n#### Code\n\n```ts\nimport { Controller, Injectable, Get } from '@nestjs/common'\nimport { InjectRepository } from '@nestjs/typeorm'\nimport { FilterOperator, FilterSuffix, Paginate, PaginateQuery, paginate, Paginated } from 'nestjs-paginate'\nimport { Repository, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'\n\n@Entity()\nexport class CatEntity {\n  @PrimaryGeneratedColumn()\n  id: number\n\n  @Column('text')\n  name: string\n\n  @Column('text')\n  color: string\n\n  @Column('int')\n  age: number\n\n  @Column({ nullable: true })\n  lastVetVisit: Date | null\n\n  @CreateDateColumn()\n  createdAt: string\n}\n\n@Injectable()\nexport class CatsService {\n  constructor(\n    @InjectRepository(CatEntity)\n    private readonly catsRepository: Repository\u003cCatEntity\u003e\n  ) {}\n\n  public findAll(query: PaginateQuery): Promise\u003cPaginated\u003cCatEntity\u003e\u003e {\n    return paginate(query, this.catsRepository, {\n      sortableColumns: ['id', 'name', 'color', 'age'],\n      nullSort: 'last',\n      defaultSortBy: [['id', 'DESC']],\n      searchableColumns: ['name', 'color', 'age'],\n      select: ['id', 'name', 'color', 'age', 'lastVetVisit'],\n      filterableColumns: {\n        name: [FilterOperator.EQ, FilterSuffix.NOT],\n        age: true,\n      },\n    })\n  }\n}\n\n@Controller('cats')\nexport class CatsController {\n  constructor(private readonly catsService: CatsService) {}\n\n  @Get()\n  public findAll(@Paginate() query: PaginateQuery): Promise\u003cPaginated\u003cCatEntity\u003e\u003e {\n    return this.catsService.findAll(query)\n  }\n}\n```\n\n### Config\n\n````ts\nconst paginateConfig: PaginateConfig\u003cCatEntity\u003e {\n  /**\n   * Required: true (must have a minimum of one column)\n   * Type: (keyof CatEntity)[]\n   * Description: These are the columns that are valid to be sorted by.\n   */\n  sortableColumns: ['id', 'name', 'color'],\n\n  /**\n   * Required: false\n   * Type: 'first' | 'last'\n   * Description: Define whether to put null values at the beginning\n   * or end of the result set.\n   */\n  nullSort: 'last',\n\n  /**\n   * Required: false\n   * Type: [keyof CatEntity, 'ASC' | 'DESC'][]\n   * Default: [[sortableColumns[0], 'ASC]]\n   * Description: The order to display the sorted entities.\n   */\n  defaultSortBy: [['name', 'DESC']],\n\n  /**\n   * Required: false\n   * Type: (keyof CatEntity)[]\n   * Description: These columns will be searched through when using the search query\n   * param. Limit search scope further by using `searchBy` query param.\n   */\n  searchableColumns: ['name', 'color'],\n\n  /**\n   * Required: false\n   * Type: (keyof CatEntity)[]\n   * Default: None\n   * Description: TypeORM partial selection. Limit selection further by using `select` query param.\n   * https://typeorm.io/select-query-builder#partial-selection\n   * Note: if you do not contain the primary key in the select array, primary key will be added automatically.\n   * \n   * Wildcard support:\n   * - Use '*' to select all columns from the main entity.\n   * - Use 'relation.*' to select all columns from a relation.\n   * - Use 'relation.subrelation.*' to select all columns from nested relations.\n   * \n   * Examples:\n   * select: ['*'] - Selects all columns from main entity\n   * select: ['id', 'name', 'toys.*'] - Selects id, name from main entity and all columns from toys relation\n   * select: ['*', 'toys.*'] - Selects all columns from both main entity and toys relation\n   */\n  select: ['id', 'name', 'color'],\n\n  /**\n   * Required: false\n   * Type: number\n   * Default: 100\n   * Description: The maximum amount of entities to return per page.\n   * Set it to -1, in conjunction with limit=-1 on query param, to disable pagination.\n   */\n  maxLimit: 20,\n\n  /**\n   * Required: false\n   * Type: number\n   * Default: 20\n   */\n  defaultLimit: 50,\n\n  /**\n   * Required: false\n   * Type: TypeORM find options\n   * Default: None\n   * https://typeorm.io/#/find-optionsfind-options.md\n   */\n  where: { color: 'ginger' },\n\n  /**\n   * Required: false\n   * Type: { [key in CatEntity]?: FilterOperator[] } - Operators based on TypeORM find operators\n   * Default: None\n   * https://typeorm.io/#/find-options/advanced-options\n   */\n  filterableColumns: { age: [FilterOperator.EQ, FilterOperator.IN] },\n\n  /**\n   * Required: false\n   * Type: RelationColumn\u003cCatEntity\u003e\n   * Description: Indicates what relations of entity should be loaded.\n   */\n  relations: [],\n\n  /**\n   * Required: false\n   * Type: boolean\n   * Default: false\n   * Description: Load eager relations using TypeORM's eager property.\n   * Only works if `relations` is not defined.\n   */\n  loadEagerRelations: true,\n\n  /**\n   * Required: false\n   * Type: boolean\n   * Description: Disables the global condition of \"non-deleted\" for the entity with delete date columns.\n   * https://typeorm.io/select-query-builder#querying-deleted-rows\n   */\n  withDeleted: false,\n\n  /**\n   * Required: false\n   * Type: boolean\n   * Description: Allows to specify withDeleted in query params to retrieve soft deleted records, convinient when you have archive functionality and some toggle to show or hide them. If not enabled explicitly the withDeleted query param will be ignored.\n   */\n  allowWithDeletedInQuery: false,\n\n  /**\n   * Required: false\n   * Type: string\n   * Description: Allow user to choose between limit/offset and take/skip, or cursor-based pagination.\n   * Default: PaginationType.TAKE_AND_SKIP\n   * Options: PaginationType.LIMIT_AND_OFFSET, PaginationType.TAKE_AND_SKIP, PaginationType.CURSOR\n   *\n   * However, using limit/offset can cause problems with relations.\n   */\n  paginationType: PaginationType.LIMIT_AND_OFFSET,\n\n  /**\n   * Required: false\n   * Type: boolean\n   * Default: false\n   * Description: Generate relative paths in the resource links.\n   */\n  relativePath: true,\n\n  /**\n   * Required: false\n   * Type: string\n   * Description: Overrides the origin of absolute resource links if set.\n   */\n  origin: 'http://cats.example',\n\n  /**\n   * Required: false\n   * Type: boolean\n   * Default: false\n   * Description: Prevent `searchBy` query param from limiting search scope further. Search will depend upon `searchableColumns` config option only\n   */\n  ignoreSearchByInQueryParam: true,\n\n  /**\n   * Required: false\n   * Type: boolean\n   * Default: false\n   * Description: Prevent `select` query param from limiting selection further. Partial selection will depend upon `select` config option only\n   */\n  ignoreSelectInQueryParam: true,\n\n  /**\n   * Required: false\n   * Type: 'leftJoinAndSelect' | 'innerJoinAndSelect'\n   * Default: 'leftJoinAndSelect'\n   * Description: Relationships will be joined with either LEFT JOIN or INNER JOIN, and their columns selected. Can be specified per column with `joinMethods` configuration.\n   */\n  defaultJoinMethod: 'leftJoinAndSelect',\n\n  /**\n   * Required: false\n   * Type: MappedColumns\u003cT, JoinMethod\u003e\n   * Default: false\n   * Description: Overrides the join method per relationship.\n   */\n  joinMethods: {age: 'innerJoinAndSelect', size: 'leftJoinAndSelect'},\n\n  /**\n   * Required: false\n   * Type: boolean\n   * Default: false\n   * Description: Enable multi-word search behavior. When true, each word in the search query\n   * will be treated as a separate search term, allowing for more flexible matching.\n   */\n  multiWordSearch: false,\n\n  /**\n   * Required: false\n   * Type: (qb: SelectQueryBuilder\u003cT\u003e) =\u003e SelectQueryBuilder\u003cany\u003e\n   * Default: undefined\n   * Description: Callback that lets you override the COUNT query executed by\n   * paginate(). The function receives a **clone** of the original QueryBuilder,\n   * so it already contains every WHERE clause and parameter parsed by\n   * nestjs-paginate.\n   *\n   * Typical use-case: remove expensive LEFT JOINs or build a lighter DISTINCT\n   * count when getManyAndCount() becomes a bottleneck.\n   *\n   * Example:\n   * ```ts\n   * buildCountQuery: qb =\u003e {\n   *   qb.expressionMap.joinAttributes = [];   // drop all joins\n   *   qb.select('p.id').distinct(true);       // keep DISTINCT on primary key\n   *   return qb;                              // paginate() will call .getCount()\n   * }\n   * ```\n   */\n  buildCountQuery: (qb: SelectQueryBuilder\u003cT\u003e) =\u003e SelectQueryBuilder\u003cany\u003e,\n}\n````\n\n## Usage with Query Builder\n\nYou can paginate custom queries by passing on the query builder:\n\n### Example\n\n```typescript\nconst queryBuilder = repo\n  .createQueryBuilder('cats')\n  .leftJoinAndSelect('cats.owner', 'owner')\n  .where('cats.owner = :ownerId', { ownerId })\n\nconst result = await paginate\u003cCatEntity\u003e(query, queryBuilder, config)\n```\n\n## Usage with Relations\n\nSimilar as with repositories, you can utilize `relations` as a simplified left-join form:\n\n### Example\n\n#### Endpoint\n\n```url\nhttp://localhost:3000/cats?filter.toys.name=$in:Mouse,String\n```\n\n#### Code\n\n```typescript\nconst config: PaginateConfig\u003cCatEntity\u003e = {\n  relations: ['toys'],\n  sortableColumns: ['id', 'name', 'toys.name'],\n  filterableColumns: {\n    'toys.name': [FilterOperator.IN],\n  },\n}\n\nconst result = await paginate\u003cCatEntity\u003e(query, catRepo, config)\n```\n\n**Note:** Embedded columns on relations have to be wrapped with brackets:\n\n```typescript\nconst config: PaginateConfig\u003cCatEntity\u003e = {\n  sortableColumns: ['id', 'name', 'toys.(size.height)', 'toys.(size.width)'],\n  searchableColumns: ['name'],\n  relations: ['toys'],\n}\n```\n\n## Usage with Nested Relations\n\nSimilar as with relations, you can specify nested relations for sorting, filtering and searching:\n\n### Example\n\n#### Endpoint\n\n```url\nhttp://localhost:3000/cats?filter.home.pillows.color=pink\n```\n\n#### Code\n\n```typescript\nconst config: PaginateConfig\u003cCatEntity\u003e = {\n  relations: { home: { pillows: true } },\n  sortableColumns: ['id', 'name', 'home.pillows.color'],\n  searchableColumns: ['name', 'home.pillows.color'],\n  filterableColumns: {\n    'home.pillows.color': [FilterOperator.EQ],\n  },\n}\n\nconst result = await paginate\u003cCatEntity\u003e(query, catRepo, config)\n```\n\n## Usage with Eager Loading\n\nEager loading should work with TypeORM's eager property out of the box:\n\n### Example\n\n#### Code\n\n```typescript\n@Entity()\nexport class CatEntity {\n  // ...\n\n  @OneToMany(() =\u003e CatToyEntity, (catToy) =\u003e catToy.cat, {\n    eager: true,\n  })\n  toys: CatToyEntity[]\n}\n\nconst config: PaginateConfig\u003cCatEntity\u003e = {\n  loadEagerRelations: true,\n  sortableColumns: ['id', 'name', 'toys.name'],\n  filterableColumns: {\n    'toys.name': [FilterOperator.IN],\n  },\n}\n\nconst result = await paginate\u003cCatEntity\u003e(query, catRepo, config)\n```\n\n## Filters\n\nFilter operators must be whitelisted per column in `PaginateConfig`.\n\n### Examples\n\n#### Code\n\n```typescript\nconst config: PaginateConfig\u003cCatEntity\u003e = {\n  // ...\n  filterableColumns: {\n    // Enable individual operators on a column\n    id: [FilterOperator.EQ, FilterSuffix.NOT],\n\n    // Enable all operators on a column\n    age: true,\n  },\n}\n```\n\n`?filter.name=$eq:Milo` is equivalent with `?filter.name=Milo`\n\n`?filter.age=$btw:4,6` where column `age` is between `4` and `6`\n\n`?filter.id=$not:$in:2,5,7` where column `id` is **not** `2`, `5` or `7`\n\n`?filter.summary=$not:$ilike:term` where column `summary` does **not** contain `term`\n\n`?filter.summary=$sw:term` where column `summary` starts with `term`\n\n`?filter.seenAt=$null` where column `seenAt` is `NULL`\n\n`?filter.seenAt=$not:$null` where column `seenAt` is **not** `NULL`\n\n`?filter.createdAt=$btw:2022-02-02,2022-02-10` where column `createdAt` is between the dates `2022-02-02` and `2022-02-10`\n\n`?filter.createdAt=$lt:2022-12-20T10:00:00.000Z` where column `createdAt` is before iso date `2022-12-20T10:00:00.000Z`\n\n`?filter.roles=$contains:moderator` where column `roles` is an array and contains the value `moderator`\n\n`?filter.roles=$contains:moderator,admin` where column `roles` is an array and contains the values `moderator` and `admin`\n\n## JSONB Filters\n\nYou can filter on JSONB columns using dot notation to access nested fields.\n\n### Supported operators\n\n| Operator | Description |\n|----------|-------------|\n| `$eq` | Exact match (`column @\u003e '{\"key\":\"value\"}'`) |\n| `$in` | Match any of a comma-separated list of values |\n\n\u003e **Note:** JSONB filtering is implemented using PostgreSQL's `@\u003e` (containment) operator and is only supported by **PostgreSQL**.\n\n### Direct JSONB column\n\n```\n?filter.metadata.enabled=$eq:true\n```\n\nwhere `metadata` is a JSONB column and the filter matches rows whose `metadata` object contains `{ \"enabled\": true }`.\n\n### JSONB column through a relation\n\nUse the same dot notation to traverse relations before accessing the JSONB field:\n\n```\n?filter.settings.theme=$eq:dark\n```\n\nwhere `settings` is a relation whose JSONB column `theme` is filtered.\n\n```typescript\nconst config: PaginateConfig\u003cUserEntity\u003e = {\n  relations: ['settings'],\n  filterableColumns: {\n    'settings.theme': [FilterOperator.EQ, FilterOperator.IN],\n  },\n}\n```\n\n### Deeply nested JSONB paths\n\nPaths inside the JSON value itself can be arbitrarily deep:\n\n```\n?filter.settings.ui.sidebar.color=$eq:blue\n```\n\nRegardless of nesting depth, the library walks TypeORM entity metadata to determine where the relation chain ends and the JSON key path begins, then builds the correct `@\u003e` containment expression automatically.\n\n### `$in` operator on JSONB\n\n```\n?filter.metadata.status=$in:active,pending\n?filter.settings.theme=$in:dark,light\n```\n\nEach value is expanded into its own `@\u003e` condition joined with `OR`:\n\n```sql\n(col @\u003e '{\"status\":\"active\"}' OR col @\u003e '{\"status\":\"pending\"}')\n```\n\n`$not:$in` is also supported and produces `NOT` conditions joined with `AND`:\n\n```\n?filter.metadata.status=$not:$in:banned,suspended\n```\n\n## Multi Filters\n\nMulti filters are filters that can be applied to a single column with a comparator.\n\n### Examples\n\n`?filter.createdAt=$gt:2022-02-02\u0026filter.createdAt=$lt:2022-02-10` where column `createdAt` is after `2022-02-02` **and** before `2022-02-10`\n\n`?filter.roles=$contains:moderator\u0026filter.roles=$or:$contains:admin` where column `roles` is an array and contains `moderator` **or** `admin`\n\n`?filter.id=$gt:3\u0026filter.id=$and:$lt:5\u0026filter.id=$or:$eq:7` where column `id` is greater than `3` **and** less than `5` **or** equal to `7`\n\n**Note:** The `$and` comparators are not required. The above example is equivalent to:\n\n`?filter.id=$gt:3\u0026filter.id=$lt:5\u0026filter.id=$or:$eq:7`\n\n**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.\n\n`...\u0026filter.id=5\u0026filter.id=$or:7\u0026filter.name=Milo\u0026...`\n\nis resolved to:\n\n`WHERE ... AND (id = 5 OR id = 7) AND name = 'Milo' AND ...`\n\n## Cursor-based Pagination\n\n- `paginationType: PaginationType.CURSOR`\n- Cursor format:\n  - Numbers: `[prefix1][integer:11 digits][prefix2][decimal:4 digits]` (e.g., `Y00000000001V2500` for -1.25 in ASC).\n  - Dates: `[prefix][value:15 digits]` (e.g., `V001671444000000` for a timestamp in DESC).\n- Prefixes:\n  - `null`: `A` (lowest priority, last in results).\n  - ASC:\n    - positive-int: `V` (greater than or equal to 1), `X` (less than 1)\n    - positive-decimal: `V` (not zero), `X` (zero)\n    - zero-int: `X`\n    - zero-decimal: `X`\n    - negative-int: `Y`\n    - negative-decimal: `V`\n  - DESC:\n    - positive-int: `V`\n    - positive-decimal: `V`\n    - zero-int: `N`\n    - zero-decimal: `X`\n    - negative-int: `M` (less than or equal to -1), `N` (greater than -1)\n    - negative-decimal: `V` (not zero), `X` (zero)\n- Logic:\n  - Numbers: Split into integer (11 digits) and decimal (4 digits) parts, with separate prefixes. Supports negative values, with sorting adjusted per direction.\n  - Dates: Single prefix with 15-digit timestamp padded with zeros.\n  - ASC: Negative → Zero → Positive → Null.\n  - DESC: Positive → Zero → Negative → Null.\n- Notes:\n  - Multiple columns: `sortBy` can include multiple columns to create and sort by the cursor (e.g., `sortBy=age:ASC\u0026sortBy=createdAt:DESC`), but at least one column must be unique to ensure consistent ordering.\n  - Supported columns: Cursor sorting is available for numeric and date-related columns (string columns are not supported).\n  - Decimal support: Numeric columns can include decimals, limited to 11 digits for the integer part and 4 digits for the decimal part.\n\n## Swagger\n\nYou can use two default decorators @ApiOkResponsePaginated and @ApiPagination to generate swagger documentation for your endpoints\n\n`@ApiOkPaginatedResponse` is for response body, return http[](https://) status is 200\n\n`@ApiPaginationQuery` is for query params\n\n```typescript\n  @Get()\n  @ApiOkPaginatedResponse(\n    UserDto,\n    USER_PAGINATION_CONFIG,\n  )\n  @ApiPaginationQuery(USER_PAGINATION_CONFIG)\n  async findAll(\n    @Paginate()\n    query: PaginateQuery,\n  ): Promise\u003cPaginated\u003cUserEntity\u003e\u003e {\n\n  }\n```\n\nThere is also some syntax sugar for this, and you can use only one decorator `@PaginatedSwaggerDocs` for both response body and query params\n\n```typescript\n  @Get()\n  @PaginatedSwaggerDocs(UserDto, USER_PAGINATION_CONFIG)\n  async findAll(\n    @Paginate()\n    query: PaginateQuery,\n  ): Promise\u003cPaginated\u003cUserEntity\u003e\u003e {\n\n  }\n```\n\nIt is also possible to customize a swagger UI completely or partially, by following the default implementation and creating your own version of PaginatedSwaggerDocs decorator\n\nLet's say you want some custom appearance for SortBy, you need to create a decorator for it\n\n```typescript\nexport function CustomSortBy(paginationConfig: PaginateConfig\u003cany\u003e) {\n  return ApiQuery({\n    name: 'sortBy',\n    isArray: true,\n    description: `My custom sort by description`,\n    required: false,\n    type: 'string',\n  })\n}\n```\n\nNow you can create your version of the whole docs decorator and use it\n\n```typescript\n\nconst CustomApiPaginationQuery = (paginationConfig: PaginateConfig\u003cany\u003e) =\u003e {\n  return applyDecorators(\n    ...[\n      Page(),\n      Limit(paginationConfig),\n      Where(paginationConfig),\n      CustomSortBy(paginationConfig),\n      Search(paginationConfig),\n      SearchBy(paginationConfig),\n      Select(paginationConfig),\n    ].filter((v): v is MethodDecorator =\u003e v !== undefined)\n  )\n}\n\nfunction CustomPaginatedSwaggerDocs\u003cDTO extends Type\u003cunknown\u003e\u003e(dto: DTO, paginatedConfig: PaginateConfig\u003cany\u003e) {\n  return applyDecorators(ApiOkPaginatedResponse(dto, paginatedConfig), CustomApiPaginationQuery(paginatedConfig))\n}\n\n```\n\nYou can use CustomPaginatedSwaggerDocs instead of default PaginatedSwaggerDocs\n\n\n\n## Troubleshooting\n\nThe package does not report error reasons in the response bodies. They are instead\nreported as `debug` level [logging](https://docs.nestjs.com/techniques/logger#logger).\n\nCommon errors include missing `sortableColumns` or `filterableColumns` (the latter only affects filtering).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fppetzold%2Fnestjs-paginate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fppetzold%2Fnestjs-paginate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fppetzold%2Fnestjs-paginate/lists"}