{"_id":"@ciphercross/nestjs-filters","name":"@ciphercross/nestjs-filters","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@ciphercross/nestjs-filters","version":"1.0.0","description":"NestJS filters library with DTOs, utilities, and enums for pagination, sorting, searching, and filtering. Designed to work seamlessly with Prisma ORM and @ciphercross/nestjs-location for geographic filtering.","main":"dist/index.js","types":"dist/index.d.ts","scripts":{"build":"tsc -p tsconfig.build.json","prepublishOnly":"npm run build","test":"jest","test:watch":"jest --watch"},"keywords":["nestjs","filters","pagination","sorting","search","prisma","dto","location","geographic","distance"],"author":{"name":"Viktoriia Scherba","email":"viktoriia.scherba"},"license":"MIT","peerDependencies":{"@nestjs/common":"^10.0.0 || ^11.0.0","@nestjs/swagger":"^7.0.0 || ^11.0.0","class-transformer":"^0.5.0","class-validator":"^0.14.0"},"devDependencies":{"@types/jest":"^30.0.0","@types/node":"^20.0.0","jest":"^30.2.0","ts-jest":"^29.4.5","typescript":"^5.0.0"},"repository":{"type":"git","url":"git+ssh://git@github.com/CipherCross/nestjs-filters.git"},"_id":"@ciphercross/nestjs-filters@1.0.0","gitHead":"5a929fc4de3b338fa1f28f65c4da0e8b73e37ae5","bugs":{"url":"https://github.com/CipherCross/nestjs-filters/issues"},"homepage":"https://github.com/CipherCross/nestjs-filters#readme","_nodeVersion":"20.19.5","_npmVersion":"10.8.2","dist":{"integrity":"sha512-kxJMTBYReCLsCtoxNy358u46fLO4eEtQN2GBm0QApa31MY5A7d4qSxdwEu+YA7iBJhJH/JvmkPdchbgFD+JWAw==","shasum":"3eb9ac40bb572f095ef826bd14e157ae37e9e488","tarball":"https://registry.npmjs.org/@ciphercross/nestjs-filters/-/nestjs-filters-1.0.0.tgz","fileCount":55,"unpackedSize":241623,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCMLKvtnqXAsBFqDCM0z42nPx73dn5kGFChWEr9PFFFIwIgfJnUxL8PyetRcLxz1JL8ZPMtadgS2gM4gKVE4ixTg5o="}]},"_npmUser":{"name":"mykyta-ciphercross","email":"mykyta.shevchenko@ciphercross.com"},"directories":{},"maintainers":[{"name":"mykyta-ciphercross","email":"mykyta.shevchenko@ciphercross.com"},{"name":"viktoriia.scherba","email":"viktoriia.scherba@ciphercross.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/nestjs-filters_1.0.0_1764177971659_0.5545061034383827"},"_hasShrinkwrap":false}},"time":{"created":"2025-11-26T17:26:11.508Z","1.0.0":"2025-11-26T17:26:11.899Z","modified":"2025-11-26T17:26:12.323Z"},"maintainers":[{"name":"mykyta-ciphercross","email":"mykyta.shevchenko@ciphercross.com"},{"name":"viktoriia.scherba","email":"viktoriia.scherba@ciphercross.com"}],"description":"NestJS filters library with DTOs, utilities, and enums for pagination, sorting, searching, and filtering. Designed to work seamlessly with Prisma ORM and @ciphercross/nestjs-location for geographic filtering.","homepage":"https://github.com/CipherCross/nestjs-filters#readme","keywords":["nestjs","filters","pagination","sorting","search","prisma","dto","location","geographic","distance"],"repository":{"type":"git","url":"git+ssh://git@github.com/CipherCross/nestjs-filters.git"},"author":{"name":"Viktoriia Scherba","email":"viktoriia.scherba"},"bugs":{"url":"https://github.com/CipherCross/nestjs-filters/issues"},"license":"MIT","readme":"# @ciphercross/nestjs-filters \n\nNestJS filters library with DTOs, utilities, and enums for pagination, sorting, searching, and filtering. Designed to work seamlessly with Prisma ORM.\n\n## Installation\n\n```bash\nnpm install @ciphercross/nestjs-filters\n```\n\n## Peer Dependencies\n\nThis library requires the following peer dependencies:\n\n- `@nestjs/common` (^10.0.0 || ^11.0.0)\n- `@nestjs/swagger` (^7.0.0 || ^11.0.0)\n- `class-validator` (^0.14.0)\n- `class-transformer` (^0.5.0)\n\n## Integration with @ciphercross/nestjs-location\n\nThis library works seamlessly with `@ciphercross/nestjs-location` for geographic filtering. The `GeoFiltersDto` provides DTO validation, while `@ciphercross/nestjs-location` handles the actual distance calculations and filtering.\n\n**Recommended**: Install both packages for complete geographic filtering functionality:\n\n```bash\nnpm install @ciphercross/nestjs-filters @ciphercross/nestjs-location\n```\n\n## Features\n\n- **Pagination**: Built-in pagination DTOs and utilities\n- **Sorting**: Sort field validation and Prisma orderBy builders\n- **Search**: Multi-field search filters with nested field support\n- **Date Filtering**: Date range filters and time-based filters (today, last 7 days, etc.)\n- **Numeric Ranges**: Min/max filters for numeric fields\n- **Geographic Filters**: Latitude/longitude and distance filtering\n- **Type-Safe**: Full TypeScript support with proper types\n\n## Quick Start\n\n### 1. Create a Custom Filter DTO\n\nExtend `BaseListFiltersDto` to create your own filter DTO:\n\n```typescript\nimport { BaseListFiltersDto, GeoFiltersDto } from '@ciphercross/nestjs-filters';\nimport { ApiPropertyOptional } from '@nestjs/swagger';\nimport { IsOptional, IsString } from 'class-validator';\n\nexport class ServicesFiltersDto extends BaseListFiltersDto implements GeoFiltersDto {\n  // Inherits: page, limit, sortBy, sortOrder, search\n  \n  // Geo filters\n  lat?: number;\n  lng?: number;\n  maxDistance?: number;\n\n  // Custom filters\n  @ApiPropertyOptional()\n  @IsOptional()\n  @IsString()\n  category?: string;\n\n  @ApiPropertyOptional()\n  @IsOptional()\n  @IsString()\n  businessCategory?: string;\n}\n```\n\n### 2. Use in Controller\n\n```typescript\nimport { Controller, Get, Query } from '@nestjs/common';\nimport { ServicesFiltersDto } from './dto/services-filters.dto';\n\n@Controller('services')\nexport class ServicesController {\n  @Get()\n  async findAll(@Query() filters: ServicesFiltersDto) {\n    // filters.page, filters.limit, filters.search, etc.\n  }\n}\n```\n\n### 3. Build Prisma Queries\n\n```typescript\nimport {\n  buildPagination,\n  buildPrismaOrderBy,\n  buildSearchFilter,\n  buildRangeFilter,\n} from '@ciphercross/nestjs-filters';\n\nasync getServices(filters: ServicesFiltersDto) {\n  // Pagination\n  const { skip, take } = buildPagination(filters.page, filters.limit);\n\n  // Sorting\n  const orderBy = buildPrismaOrderBy(\n    filters.sortBy,\n    filters.sortOrder,\n    ['createdAt', 'price', 'name'], // allowed fields\n    'createdAt', // default field\n    'desc' // default order\n  );\n\n  // Search\n  const searchFilter = buildSearchFilter(filters.search, ['title', 'description']);\n\n  // Numeric range\n  const priceFilter = buildRangeFilter('price', filters.minPrice, filters.maxPrice);\n\n  // Build where clause\n  const where = {\n    ...searchFilter,\n    ...priceFilter,\n    // ... other filters\n  };\n\n  return this.prisma.service.findMany({\n    where,\n    skip,\n    take,\n    orderBy,\n  });\n}\n```\n\n## API Reference\n\n### DTOs\n\n#### `PaginationFilterDto`\n\nBase pagination DTO with `page` and `limit` fields.\n\n```typescript\nclass PaginationFilterDto {\n  page?: number = 1;\n  limit?: number = 20;\n}\n```\n\n#### `SortFilterDto`\n\nSorting DTO with `sortBy` and `sortOrder` fields.\n\n```typescript\nclass SortFilterDto {\n  sortBy?: string;\n  sortOrder?: SortOrder = SortOrder.DESC;\n}\n```\n\n#### `SearchFilterDto`\n\nSearch DTO with `search` field.\n\n```typescript\nclass SearchFilterDto {\n  search?: string;\n}\n```\n\n#### `GeoFiltersDto`\n\nGeographic filters with latitude, longitude, and max distance. Designed to work with `@ciphercross/nestjs-location` for distance calculations and filtering.\n\n```typescript\nclass GeoFiltersDto {\n  lat?: number; // -90 to 90\n  lng?: number; // -180 to 180\n  maxDistance?: number; // 0.1 to 1000 km\n}\n```\n\n**Note**: This DTO only provides validation. For actual distance filtering, use `DistanceFilterService` from `@ciphercross/nestjs-location`.\n\n#### `NumericRangeFilterDto`\n\nBase numeric range filter with `min` and `max` fields.\n\n```typescript\nclass NumericRangeFilterDto {\n  min?: number;\n  max?: number;\n}\n```\n\n#### `DateRangeFilterDto`\n\nDate range filter with `startDate`, `endDate`, and `date` fields.\n\n```typescript\nclass DateRangeFilterDto {\n  startDate?: string; // ISO date string\n  endDate?: string; // ISO date string\n  date?: string; // Single date (ISO date string)\n}\n```\n\n#### `BaseListFiltersDto`\n\nCombines pagination, sorting, and search. Extend this for your custom filters.\n\n```typescript\nclass BaseListFiltersDto extends PaginationFilterDto \n  implements SortFilterDto, SearchFilterDto {\n  sortBy?: string;\n  sortOrder?: 'asc' | 'desc';\n  search?: string;\n}\n```\n\n### Enums\n\n#### `SortOrder`\n\n```typescript\nenum SortOrder {\n  ASC = 'asc',\n  DESC = 'desc',\n}\n```\n\n#### `TimeFilter`\n\n```typescript\nenum TimeFilter {\n  ALL = 'all',\n  TODAY = 'today',\n  LAST_7_DAYS = 'last_7_days',\n  LAST_30_DAYS = 'last_30_days',\n  EARLIER = 'earlier',\n}\n```\n\n### Utilities\n\n#### `buildPagination(page?, limit?)`\n\nBuilds Prisma pagination parameters.\n\n```typescript\nconst { skip, take } = buildPagination(1, 20);\n// Returns: { skip: 0, take: 20 }\n```\n\n#### `buildPrismaOrderBy(sortBy, sortOrder, allowedFields, defaultField, defaultOrder)`\n\nBuilds Prisma orderBy object with validation.\n\n```typescript\nconst orderBy = buildPrismaOrderBy(\n  'price',\n  'asc',\n  ['createdAt', 'price', 'name'],\n  'createdAt',\n  'desc'\n);\n// Returns: { price: 'asc' }\n```\n\n#### `buildSearchFilter(searchTerm, fields[])`\n\nBuilds Prisma search filter for multiple fields.\n\n```typescript\nconst searchFilter = buildSearchFilter('spa', ['name', 'description']);\n// Returns: {\n//   OR: [\n//     { name: { contains: 'spa', mode: 'insensitive' } },\n//     { description: { contains: 'spa', mode: 'insensitive' } }\n//   ]\n// }\n```\n\n#### `buildNestedSearchFilter(searchTerm, nestedFields[])`\n\nBuilds Prisma search filter for nested fields.\n\n```typescript\nconst nestedFilter = buildNestedSearchFilter('spa', [\n  { path: ['business'], field: 'name' },\n  { path: ['category'], field: 'title' }\n]);\n```\n\n#### `buildRangeFilter(field, min?, max?)`\n\nBuilds Prisma numeric range filter.\n\n```typescript\nconst priceFilter = buildRangeFilter('price', 10, 100);\n// Returns: { price: { gte: 10, lte: 100 } }\n```\n\n#### `buildPrismaDateRangeFilter(field, startDate?, endDate?, singleDate?)`\n\nBuilds Prisma date range filter.\n\n```typescript\nconst dateFilter = buildPrismaDateRangeFilter(\n  'createdAt',\n  '2024-01-01',\n  '2024-12-31'\n);\n// Returns: { createdAt: { gte: Date, lte: Date } }\n```\n\n#### `buildTimeFilterWhere(field, timeFilter?)`\n\nBuilds Prisma where clause from TimeFilter enum.\n\n```typescript\nconst timeFilter = buildTimeFilterWhere('createdAt', TimeFilter.LAST_7_DAYS);\n// Returns: { createdAt: { gte: Date } }\n```\n\n#### `validateSortField(sortBy, allowedFields, defaultField)`\n\nValidates sort field against allowed fields.\n\n```typescript\nconst field = validateSortField('price', ['createdAt', 'price'], 'createdAt');\n```\n\n#### `validateSortOrder(sortOrder, defaultOrder)`\n\nValidates sort order.\n\n```typescript\nconst order = validateSortOrder('asc', 'desc');\n// Returns: 'asc'\n```\n\n## Examples\n\n### Example 1: Services with Multiple Filters (with Location)\n\n```typescript\nimport {\n  BaseListFiltersDto,\n  GeoFiltersDto,\n  buildPagination,\n  buildPrismaOrderBy,\n  buildSearchFilter,\n  buildRangeFilter,\n} from '@ciphercross/nestjs-filters';\nimport { DistanceFilterService } from '@ciphercross/nestjs-location';\n\nexport class ServicesFiltersDto extends BaseListFiltersDto implements GeoFiltersDto {\n  lat?: number;\n  lng?: number;\n  maxDistance?: number;\n  minPrice?: number;\n  maxPrice?: number;\n  category?: string;\n}\n\n// In service\n@Injectable()\nexport class ServicesService {\n  constructor(\n    private readonly prisma: PrismaService,\n    private readonly distanceFilterService: DistanceFilterService,\n  ) {}\n\n  async getServices(filters: ServicesFiltersDto) {\n    // Build Prisma query filters\n    const where = {\n      ...buildSearchFilter(filters.search, ['title', 'description']),\n      ...buildRangeFilter('price', filters.minPrice, filters.maxPrice),\n      ...(filters.category && { category: { slug: filters.category } }),\n    };\n\n    const orderBy = buildPrismaOrderBy(\n      filters.sortBy,\n      filters.sortOrder,\n      ['createdAt', 'price'],\n      'createdAt'\n    );\n\n    // Fetch data from database\n    const services = await this.prisma.service.findMany({\n      where,\n      include: { location: true }, // Include location data\n    });\n\n    // Apply geographic filtering if coordinates provided\n    let filteredServices = services;\n    if (filters.lat && filters.lng && filters.maxDistance) {\n      filteredServices = this.distanceFilterService.applyDistanceFilter(\n        services,\n        filters.lat,\n        filters.lng,\n        filters.maxDistance,\n      );\n    }\n\n    // Apply pagination after filtering\n    const { skip, take } = buildPagination(filters.page, filters.limit);\n    const paginatedServices = filteredServices.slice(skip, skip + take);\n\n    return {\n      data: paginatedServices,\n      total: filteredServices.length,\n      page: filters.page || 1,\n      limit: filters.limit || 20,\n    };\n  }\n}\n```\n\n### Example 2: Visits with Time Filter\n\n```typescript\nimport {\n  PaginationFilterDto,\n  TimeFilter,\n  buildTimeFilterWhere,\n  buildPagination,\n} from '@ciphercross/nestjs-filters';\nimport { IsOptional, IsEnum, IsString } from 'class-validator';\n\nexport class VisitFiltersDto extends PaginationFilterDto {\n  @IsOptional()\n  @IsEnum(TimeFilter)\n  timeFilter?: TimeFilter = TimeFilter.ALL;\n\n  @IsOptional()\n  @IsString()\n  businessId?: string;\n}\n\n// In service\nasync getVisits(filters: VisitFiltersDto) {\n  const { skip, take } = buildPagination(filters.page, filters.limit);\n  \n  const where = {\n    ...buildTimeFilterWhere('createdAt', filters.timeFilter),\n    ...(filters.businessId && { businessId: filters.businessId }),\n  };\n\n  return this.prisma.visit.findMany({ where, skip, take });\n}\n```\n\n### Example 3: Reviews with Rating Range\n\n```typescript\nimport {\n  BaseListFiltersDto,\n  NumericRangeFilterDto,\n  buildRangeFilter,\n} from '@ciphercross/nestjs-filters';\n\nexport class ReviewFiltersDto extends BaseListFiltersDto implements NumericRangeFilterDto {\n  min?: number; // minRating\n  max?: number; // maxRating\n  businessId?: string;\n}\n\n// In service\nasync getReviews(filters: ReviewFiltersDto) {\n  const { skip, take } = buildPagination(filters.page, filters.limit);\n  \n  const where = {\n    ...buildRangeFilter('rating', filters.min, filters.max),\n    ...(filters.businessId && { businessId: filters.businessId }),\n  };\n\n  return this.prisma.review.findMany({ where, skip, take });\n}\n```\n\n## Integration Example: Full Location-Based Search\n\nComplete example using both `@ciphercross/nestjs-filters` and `@ciphercross/nestjs-location`:\n\n```typescript\nimport { Module } from '@nestjs/common';\nimport { LocationModule } from '@ciphercross/nestjs-location';\nimport {\n  BaseListFiltersDto,\n  GeoFiltersDto,\n  buildPagination,\n  buildSearchFilter,\n} from '@ciphercross/nestjs-filters';\nimport { LocationService } from '@ciphercross/nestjs-location';\n\n@Module({\n  imports: [LocationModule.forRoot()],\n})\nexport class AppModule {}\n\n// DTO\nexport class BusinessFiltersDto extends BaseListFiltersDto implements GeoFiltersDto {\n  lat?: number;\n  lng?: number;\n  maxDistance?: number;\n  category?: string;\n}\n\n// Service\n@Injectable()\nexport class BusinessService {\n  constructor(\n    private readonly prisma: PrismaService,\n    private readonly locationService: LocationService,\n  ) {}\n\n  async findNearbyBusinesses(filters: BusinessFiltersDto) {\n    // 1. Build base Prisma filters\n    const where = {\n      ...buildSearchFilter(filters.search, ['name', 'description']),\n      ...(filters.category && { categories: { some: { slug: filters.category } } }),\n    };\n\n    // 2. Fetch businesses with location data\n    const businesses = await this.prisma.business.findMany({\n      where,\n      include: { location: true },\n    });\n\n    // 3. Apply location filtering if coordinates provided\n    if (filters.lat && filters.lng && filters.maxDistance) {\n      const result = this.locationService.findNearest(\n        businesses,\n        filters.lat,\n        filters.lng,\n        filters.maxDistance,\n        {\n          page: filters.page || 1,\n          limit: filters.limit || 20,\n          sortBy: SortBy.DISTANCE,\n        },\n      );\n\n      return result;\n    }\n\n    // 4. Apply pagination for non-location queries\n    const { skip, take } = buildPagination(filters.page, filters.limit);\n    return {\n      items: businesses.slice(skip, skip + take),\n      total: businesses.length,\n      page: filters.page || 1,\n      limit: filters.limit || 20,\n    };\n  }\n}\n```\n\n## License\n\nUNLICENSED\n\n","readmeFilename":"README.md","_rev":"1-5118eac3a00fa696d32796ae6f6cc50d"}