{"_id":"@ai-em/nestjs-paginate","_rev":"1-5f9ef3bd073fa19f1f54daa73ddd88c2","name":"@ai-em/nestjs-paginate","dist-tags":{"latest":"10.0.0"},"versions":{"1.1.0":{"name":"@ai-em/nestjs-paginate","version":"1.1.0","author":{"name":"Philipp Petzold","email":"ppetzold@protonmail.com"},"license":"MIT","main":"lib/index.js","typings":"lib/index.d.ts","description":"Pagination and filtering helper method for TypeORM repostiories or query builders using Nest.js framework.","keywords":["nestjs","typeorm","express","pagination","paginate","filtering","search"],"scripts":{"prebuild":"rimraf lib","build":"tsc","prepare":"tsc","dev:yalc":"nodemon --watch src --ext ts --exec 'npm run build && yalc push'","format":"prettier --write \"src/**/*.ts\"","format:ci":"prettier --list-different \"src/**/*.ts\"","lint":"eslint -c .eslintrc.json --ext .ts --max-warnings 0 src","test":"jest","test:watch":"jest --watch","test:cov":"jest --coverage","test:debug":"node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand"},"devDependencies":{"@nestjs/common":"^10.0.0","@types/express":"^4.17.17","@types/jest":"^29.5.2","@types/lodash":"^4.14.195","@types/node":"^18.16.19","@typescript-eslint/eslint-plugin":"^5.61.0","@typescript-eslint/parser":"^5.61.0","eslint":"^8.44.0","eslint-config-prettier":"^8.8.0","eslint-plugin-prettier":"^4.2.1","fastify":"^4.19.2","jest":"^29.5.0","pg":"^8.11.1","prettier":"^2.8.8","reflect-metadata":"^0.1.13","rxjs":"^7.8.1","sqlite3":"^5.1.6","ts-jest":"^29.1.1","ts-node":"^10.9.1","typeorm":"^0.3.17","typescript":"^4.9.5"},"dependencies":{"lodash":"^4.17.21"},"peerDependencies":{"@nestjs/common":"^10.0.0","express":"^4.18.2","fastify":"^4.19.2","typeorm":"^0.3.17"},"jest":{"moduleFileExtensions":["js","json","ts"],"rootDir":"src","testRegex":".spec.ts$","transform":{"^.+\\.(t|j)s$":"ts-jest"},"coverageDirectory":"../coverage","testEnvironment":"node"},"repository":{"type":"git","url":"git+https://github.com/ppetzold/nestjs-paginate.git"},"homepage":"https://github.com/ppetzold/nestjs-paginate#readme","bugs":{"url":"https://github.com/ppetzold/nestjs-paginate/issues"},"publishConfig":{"access":"public"},"release":{"branches":["master"]},"gitHead":"c5e0be33be1ee43544d81a2bc3d8b98f15d75c11","_id":"@ai-em/nestjs-paginate@1.1.0","_nodeVersion":"18.12.1","_npmVersion":"8.19.2","dist":{"integrity":"sha512-bhZXwU68h13fJUEb4lJoedVPIjrjfjJt+trLOXgbnAaLSYncmCvHkT8klNOJlQYGLzCZvWQBgDe3IcrGecSDWA==","shasum":"d84dcf64a5f3692f3bc41819fc88ca44d9eb8092","tarball":"https://registry.npmjs.org/@ai-em/nestjs-paginate/-/nestjs-paginate-1.1.0.tgz","fileCount":40,"unpackedSize":461735,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDuQc5i4/UGAZ5I/73hOomT47pn2i++I2Gbyp547mYcmgIhANqMCWD3l/WdZlEmYwxrv295SpYM74v3o6AZtXUu1J3a"}]},"_npmUser":{"name":"ai-energy-miracle","email":"sunwei415@126.com"},"directories":{},"maintainers":[{"name":"ai-energy-miracle","email":"sunwei415@126.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nestjs-paginate_1.1.0_1688435108696_0.7584150900655875"},"_hasShrinkwrap":false},"10.0.0":{"name":"@ai-em/nestjs-paginate","version":"10.0.0","author":{"name":"Philipp Petzold","email":"ppetzold@protonmail.com"},"license":"MIT","main":"lib/index.js","typings":"lib/index.d.ts","description":"Pagination and filtering helper method for TypeORM repostiories or query builders using Nest.js framework.","keywords":["nestjs","typeorm","express","pagination","paginate","filtering","search"],"scripts":{"prebuild":"rimraf lib","build":"tsc","prepare":"tsc","dev:yalc":"nodemon --watch src --ext ts --exec 'npm run build && yalc push'","format":"prettier --write \"src/**/*.ts\"","format:ci":"prettier --list-different \"src/**/*.ts\"","lint":"eslint -c .eslintrc.json --ext .ts --max-warnings 0 src","test":"jest","test:watch":"jest --watch","test:cov":"jest --coverage","test:debug":"node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand"},"devDependencies":{"@nestjs/common":"^10.0.0","@types/express":"^4.17.17","@types/jest":"^29.5.2","@types/lodash":"^4.14.195","@types/node":"^18.16.19","@typescript-eslint/eslint-plugin":"^5.61.0","@typescript-eslint/parser":"^5.61.0","eslint":"^8.44.0","eslint-config-prettier":"^8.8.0","eslint-plugin-prettier":"^4.2.1","fastify":"^4.19.2","jest":"^29.5.0","pg":"^8.11.1","prettier":"^2.8.8","reflect-metadata":"^0.1.13","rxjs":"^7.8.1","sqlite3":"^5.1.6","ts-jest":"^29.1.1","ts-node":"^10.9.1","typeorm":"^0.3.17","typescript":"^4.9.5"},"dependencies":{"lodash":"^4.17.21"},"peerDependencies":{"@nestjs/common":"^10.0.0","express":"^4.18.2","fastify":"^4.19.2","typeorm":"^0.3.17"},"jest":{"moduleFileExtensions":["js","json","ts"],"rootDir":"src","testRegex":".spec.ts$","transform":{"^.+\\.(t|j)s$":"ts-jest"},"coverageDirectory":"../coverage","testEnvironment":"node"},"repository":{"type":"git","url":"git+https://github.com/ppetzold/nestjs-paginate.git"},"homepage":"https://github.com/ppetzold/nestjs-paginate#readme","bugs":{"url":"https://github.com/ppetzold/nestjs-paginate/issues"},"publishConfig":{"access":"public"},"release":{"branches":["master"]},"gitHead":"96df4be59d168861df445ebe0c97fe511db67de7","_id":"@ai-em/nestjs-paginate@10.0.0","_nodeVersion":"18.12.1","_npmVersion":"8.19.2","dist":{"integrity":"sha512-WdPndiGyYZHhSYsVstonR7cgHxGapufFcs7RjCLBdFkveSRzXTzwnoH6oSBS4aeD4KqFhsBMLmtkd7orpBITMw==","shasum":"520eeb5337d2b29512f3d98d0f4d8f92a1904697","tarball":"https://registry.npmjs.org/@ai-em/nestjs-paginate/-/nestjs-paginate-10.0.0.tgz","fileCount":40,"unpackedSize":461736,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB3OeE2oRatqEn8qkV2Aq3wT1kIAP/Eh3IJx0PqNkmwvAiB8jDgvy5YfxUSGyRp03Nc9SL1cxDdpFS94+1U4+Q7hRA=="}]},"_npmUser":{"name":"ai-energy-miracle","email":"sunwei415@126.com"},"directories":{},"maintainers":[{"name":"ai-energy-miracle","email":"sunwei415@126.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nestjs-paginate_10.0.0_1688435229441_0.30267311250118123"},"_hasShrinkwrap":false}},"time":{"created":"2023-07-04T01:45:08.621Z","1.1.0":"2023-07-04T01:45:08.864Z","modified":"2023-07-04T01:47:09.884Z","10.0.0":"2023-07-04T01:47:09.697Z"},"maintainers":[{"name":"ai-energy-miracle","email":"sunwei415@126.com"}],"description":"Pagination and filtering helper method for TypeORM repostiories or query builders using Nest.js framework.","homepage":"https://github.com/ppetzold/nestjs-paginate#readme","keywords":["nestjs","typeorm","express","pagination","paginate","filtering","search"],"repository":{"type":"git","url":"git+https://github.com/ppetzold/nestjs-paginate.git"},"author":{"name":"Philipp Petzold","email":"ppetzold@protonmail.com"},"bugs":{"url":"https://github.com/ppetzold/nestjs-paginate/issues"},"license":"MIT","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\n## Installation\n\n```\nnpm install nestjs-paginate\n```\n\n## Usage\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&page=2&sortBy=color:DESC&search=i&filter.age=$gte:3&select=id,name,color,age\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&page=1&sortBy=color:DESC&search=i&filter.age=$gte:3\",\n    \"previous\": \"http://localhost:3000/cats?limit=5&page=1&sortBy=color:DESC&search=i&filter.age=$gte:3\",\n    \"current\": \"http://localhost:3000/cats?limit=5&page=2&sortBy=color:DESC&search=i&filter.age=$gte:3\",\n    \"next\": \"http://localhost:3000/cats?limit=5&page=3&sortBy=color:DESC&search=i&filter.age=$gte:3\",\n    \"last\": \"http://localhost:3000/cats?limit=5&page=3&sortBy=color:DESC&search=i&filter.age=$gte:3\"\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<CatEntity>\n  ) {}\n\n  public findAll(query: PaginateQuery): Promise<Paginated<CatEntity>> {\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<Paginated<CatEntity>> {\n    return this.catsService.findAll(query)\n  }\n}\n```\n\n### Config\n\n```ts\nconst paginateConfig: PaginateConfig<CatEntity> {\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: You must include the primary key in the selection.\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 0, in conjunction with limit=0 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<CatEntity>\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: string\n   * Description: Allow user to choose between limit/offset and take/skip.\n   * Default: PaginationType.TAKE_AND_SKIP\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\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<CatEntity>(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<CatEntity> = {\n  relations: ['toys'],\n  sortableColumns: ['id', 'name', 'toys.name'],\n  filterableColumns: {\n    'toys.name': [FilterOperator.IN],\n  },\n}\n\nconst result = await paginate<CatEntity>(query, catRepo, config)\n```\n\n**Note:** Embedded columns on relations have to be wrapped with brackets:\n\n```typescript\nconst config: PaginateConfig<CatEntity> = {\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<CatEntity> = {\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<CatEntity>(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(() => CatToyEntity, (catToy) => catToy.cat, {\n    eager: true,\n  })\n  toys: CatToyEntity[]\n}\n\nconst config: PaginateConfig<CatEntity> = {\n  loadEagerRelations: true,\n  sortableColumns: ['id', 'name', 'toys.name'],\n  filterableColumns: {\n    'toys.name': [FilterOperator.IN],\n  },\n}\n\nconst result = await paginate<CatEntity>(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<CatEntity> = {\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## 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&filter.createdAt=$lt:2022-02-10` where column `createdAt` is after `2022-02-02` **and** before `2022-02-10`\n\n`?filter.id=$contains:moderator&filter.id=$or:$contains:admin` where column `roles` is an array and contains `moderator` **or** `admin`\n\n`?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`\n\n**Note:** The `$and` comparators are not required. The above example is equivalent to:\n\n`?filter.id=$gt:3&filter.id=$lt:5&filter.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`...&filter.id=5&filter.id=$or:7&filter.name=Milo&...`\n\nis resolved to:\n\n`WHERE ... AND (id = 5 OR id = 7) AND name = 'Milo' AND ...`\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","readmeFilename":"README.md"}