{"_rev":"4-82886c9be264c639a6f04bbb93be8671","time":{"created":"2024-09-10T06:44:08.407Z","modified":"2024-09-10T06:44:08.951Z","0.1.0":"2024-04-25T09:02:14.301Z","8.6.2":"2024-04-25T09:04:06.073Z","9.1.1":"2024-09-10T06:44:08.677Z"},"_id":"@egg-/nestjs-paginate","name":"@egg-/nestjs-paginate","dist-tags":{"latest":"9.1.1"},"versions":{"9.1.1":{"name":"@egg-/nestjs-paginate","version":"9.1.1","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 repositories 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.3.8","@nestjs/platform-express":"^10.3.8","@nestjs/testing":"^10.3.8","@types/express":"^4.17.21","@types/jest":"^29.5.12","@types/lodash":"^4.17.7","@types/node":"^20.16.5","@typescript-eslint/eslint-plugin":"^7.18.0","@typescript-eslint/parser":"^7.18.0","dotenv":"^16.4.5","eslint":"^8.57.0","eslint-config-prettier":"^9.1.0","eslint-plugin-prettier":"^5.2.1","fastify":"^4.26.2","jest":"^29.7.0","mysql":"^2.18.1","pg":"^8.12.0","prettier":"^3.0.3","reflect-metadata":"^0.1.14","rxjs":"^7.8.1","sqlite3":"^5.1.7","ts-jest":"^29.2.5","ts-node":"^10.9.2","typeorm":"^0.3.17","typescript":"^5.5.4"},"dependencies":{"lodash":"^4.17.21"},"peerDependencies":{"@nestjs/common":"^10.0.0","@nestjs/swagger":"^7.0.0","express":"^4.0.0","fastify":"^4.0.0","typeorm":"^0.3.17"},"jest":{"moduleFileExtensions":["js","json","ts"],"rootDir":"src","testRegex":".spec.ts$","transform":{"^.+\\.(t|j)s$":"ts-jest"},"coverageDirectory":"../coverage","testEnvironment":"node","setupFiles":["<rootDir>/../jest.setup.ts"]},"repository":{"type":"git","url":"git+https://github.com/egg-/nestjs-paginate.git"},"homepage":"https://github.com/egg-/nestjs-paginate#readme","bugs":{"url":"https://github.com/egg-/nestjs-paginate/issues"},"publishConfig":{"access":"public"},"release":{"branches":["master"]},"_id":"@egg-/nestjs-paginate@9.1.1","gitHead":"8174336ff1deb4525c1f55eadf6cab54b6b9cba9","_nodeVersion":"20.10.0","_npmVersion":"10.2.3","dist":{"integrity":"sha512-pNs4EfDvMh/ypj9vNZPMEsWLC5ZmuiscKl8Tux71lpf9YKL9WHfSXTfVZPfUCzryx8H15oEj9WPJiQLMNsinYw==","shasum":"e3ac3ed39e29516c12872102ac0cc656989955cb","tarball":"https://registry.npmjs.org/@egg-/nestjs-paginate/-/nestjs-paginate-9.1.1.tgz","fileCount":67,"unpackedSize":658955,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC1Fvit6J12CQohMy8tD4w+Y7jahqikBc+Upio5EhaC+gIgWMT6sl529adbLj89FR0e1mX4VM3KBJokcLjgeFwOfcI="}]},"_npmUser":{"name":"egg-","email":"i@egg.pe.kr"},"directories":{},"maintainers":[{"name":"egg-","email":"i@egg.pe.kr"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/nestjs-paginate_9.1.1_1725950648529_0.6462598182818933"},"_hasShrinkwrap":false}},"maintainers":[{"name":"egg-","email":"i@egg.pe.kr"}],"description":"Pagination and filtering helper method for TypeORM repositories or query builders using Nest.js framework.","homepage":"https://github.com/egg-/nestjs-paginate#readme","keywords":["nestjs","typeorm","express","pagination","paginate","filtering","search"],"repository":{"type":"git","url":"git+https://github.com/egg-/nestjs-paginate.git"},"author":{"name":"Philipp Petzold","email":"ppetzold@protonmail.com"},"bugs":{"url":"https://github.com/egg-/nestjs-paginate/issues"},"license":"MIT","readme":"# egg- Fork Version\n\n# 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 -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<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   * 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\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## 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<Paginated<UserEntity>> {\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<Paginated<UserEntity>> {\n\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","readmeFilename":"README.md"}