@nest-extended/prisma 0.0.2-beta-15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,225 @@
1
+ # @nest-extended/prisma
2
+
3
+ This package provides powerful Prisma integrations for the **NestExtended** ecosystem, offering a robust service layer with built-in pagination, filtering, soft delete capabilities, exception filters, and query utilities. Supports **PostgreSQL**, **MySQL**, and **SQLite**.
4
+
5
+ ## Key Features
6
+
7
+ ### NestService
8
+
9
+ A generic service class (`NestService<T>`) that provides:
10
+
11
+ - **CRUD Operations**: `_find`, `_get`, `_create`, `_patch`, `_remove`
12
+ - **FeathersJS-Style Querying**: Support for `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`, `$in`, `$nin`, `$like`, `$notLike`, `$iLike`, `$notILike`, `$or`, `$and`
13
+ - **Pagination**: Built-in pagination logic using `$skip` and `$limit` with configurable defaults (limit: 20, skip: 0)
14
+ - **Soft Delete**: Configurable soft delete support — marks records as deleted instead of removing, with user tracking via CLS context
15
+ - **Bulk Operations**: Optional multi-record create (enable with `multi: true`)
16
+ - **Count**: `getCount(filter)` for counting records matching a filter
17
+ - **Conditional Pagination**: `_find` accepts `{ pagination: false }` to return raw arrays instead of paginated responses
18
+ - **Relations**: `$include` for eager-loading relations (replaces Mongoose `$populate`)
19
+
20
+ **Constructor Options** (`NestServiceOptions`):
21
+ - `multi` (default: `false`) — allow bulk create with arrays
22
+ - `softDelete` (default: `true`) — enable soft delete behavior
23
+ - `pagination` (default: `true`) — enable paginated responses
24
+
25
+ ### Query Operators
26
+
27
+ All operators follow FeathersJS-style syntax and are translated to Prisma `where` clauses:
28
+
29
+ | Operator | Description | Example | Prisma Translation |
30
+ |---|---|---|---|
31
+ | `$eq` | Equality | `{ age: { $eq: 25 } }` | `{ age: 25 }` |
32
+ | `$ne` | Not equal | `{ status: { $ne: 'draft' } }` | `{ status: { not: 'draft' } }` |
33
+ | `$gt` | Greater than | `{ age: { $gt: 18 } }` | `{ age: { gt: 18 } }` |
34
+ | `$gte` | Greater than or equal | `{ age: { $gte: 18 } }` | `{ age: { gte: 18 } }` |
35
+ | `$lt` | Less than | `{ age: { $lt: 65 } }` | `{ age: { lt: 65 } }` |
36
+ | `$lte` | Less than or equal | `{ age: { $lte: 65 } }` | `{ age: { lte: 65 } }` |
37
+ | `$in` | In array | `{ role: { $in: [1, 2] } }` | `{ role: { in: [1, 2] } }` |
38
+ | `$nin` | Not in array | `{ role: { $nin: [3] } }` | `{ role: { notIn: [3] } }` |
39
+ | `$like` | Contains (case-sensitive) | `{ name: { $like: 'john' } }` | `{ name: { contains: 'john' } }` |
40
+ | `$notLike` | Does not contain | `{ name: { $notLike: 'test' } }` | `{ name: { not: { contains: 'test' } } }` |
41
+ | `$iLike` | Contains (case-insensitive) | `{ name: { $iLike: 'john' } }` | `{ name: { contains: 'john', mode: 'insensitive' } }` |
42
+ | `$notILike` | Not contains (case-insensitive) | `{ name: { $notILike: 'test' } }` | `{ NOT: { name: { contains: 'test', mode: 'insensitive' } } }` |
43
+ | `$or` | OR condition | `{ $or: [{ a: 1 }, { b: 2 }] }` | `{ OR: [{ a: 1 }, { b: 2 }] }` |
44
+ | `$and` | AND condition | `{ $and: [{ a: 1 }, { b: 2 }] }` | `{ AND: [{ a: 1 }, { b: 2 }] }` |
45
+
46
+ > **Note**: `$iLike` and `$notILike` use PostgreSQL's `mode: 'insensitive'`. MySQL is case-insensitive by default (collation-dependent). SQLite does not support case-insensitive search natively.
47
+
48
+ ### Special Parameters
49
+
50
+ | Param | Effect | Prisma Translation |
51
+ |---|---|---|
52
+ | `$sort` | Sort order — `{ createdAt: -1 }` | `orderBy: { createdAt: 'desc' }` |
53
+ | `$limit` | Max records (default: 20) | `take: number` |
54
+ | `$skip` | Skip count (default: 0) | `skip: number` |
55
+ | `$select` | Field projection (array/string/object) | `select: { field1: true, field2: true }` |
56
+ | `$include` | Eager-load relations | `include: { posts: true }` |
57
+
58
+ ### Query Utilities
59
+
60
+ - **`applyFilters(queryOptions, filters, options)`**: Applies `$select`, `$include`, `$sort`, `$limit`, `$skip` to a Prisma query options object
61
+ - **`rawQuery(query)`**: Converts FeathersJS-style query params to Prisma `where` clause
62
+ - **`assignFilters`**: Extracts known filter keys (`$sort`, `$limit`, `$skip`, `$select`, `$include`) from query params
63
+ - **`filterQuery`**: Full query parsing — separates filters from query and validates operators
64
+ - **`cleanQuery`**: Validates query operators and throws `BadRequestException` for invalid `$` params
65
+
66
+ ### Exception Filters
67
+
68
+ - **`GlobalExceptionFilter`**: Catch-all exception filter that handles:
69
+ - `HttpException` — returns standard NestJS error response
70
+ - `PrismaClientKnownRequestError` — parses specific error codes with human-readable messages
71
+ - `PrismaClientValidationError` — wraps as `BadRequestException`
72
+ - `ZodError` — wraps as `BadRequestException`
73
+ - Unhandled errors — returns 500 with stack trace (stack hidden in production)
74
+
75
+ - **`handlePrismaError(exception)`**: Translates Prisma error codes to user-friendly messages:
76
+ - `P2002` — Unique constraint violation (duplicate key)
77
+ - `P2003` — Foreign key constraint violation
78
+ - `P2025` — Record not found
79
+ - `P2014` — Relation violation
80
+ - `P2000` — Value too long for column
81
+ - `P2006` — Invalid value provided
82
+ - `P2011` — Null constraint violation
83
+ - `P2024` — Connection pool timeout
84
+ - `P2021` — Table does not exist
85
+ - `P2022` — Column does not exist
86
+
87
+ ### Types
88
+
89
+ - **`PrismaFilters`**: `$select`, `$include`, `$sort`, `$limit`, `$skip`
90
+ - **`PrismaFilterOptions`**: `defaultLimit`, `defaultSkip`, `defaultPagination`
91
+
92
+ ## Usage
93
+
94
+ ### NestService
95
+
96
+ Extend `NestService` to create a service with full CRUD capabilities.
97
+
98
+ ```typescript
99
+ import { NestService } from '@nest-extended/prisma';
100
+ import { PrismaService } from 'src/prisma/prisma.service';
101
+
102
+ @Injectable()
103
+ export class CatsService extends NestService<any> {
104
+ constructor(private readonly prisma: PrismaService) {
105
+ super(prisma.cat);
106
+ }
107
+ }
108
+ ```
109
+
110
+ With custom options:
111
+
112
+ ```typescript
113
+ super(prisma.cat, { multi: true, softDelete: false, pagination: false });
114
+ ```
115
+
116
+ ### Querying
117
+
118
+ You can use the `_find` method with FeathersJS-style query objects:
119
+
120
+ ```typescript
121
+ const results = await this.catsService._find({
122
+ name: { $iLike: 'kitty' },
123
+ age: { $gt: 5 },
124
+ $sort: { createdAt: -1 },
125
+ $limit: 10
126
+ });
127
+ ```
128
+
129
+ Disable pagination for a single query:
130
+
131
+ ```typescript
132
+ const allCats = await this.catsService._find({}, { pagination: false });
133
+ ```
134
+
135
+ ### Relations with $include
136
+
137
+ Use `$include` to eager-load related models:
138
+
139
+ ```typescript
140
+ const results = await this.usersService._find({
141
+ $include: {
142
+ posts: true
143
+ }
144
+ });
145
+
146
+ // With nested conditions
147
+ const results = await this.usersService._find({
148
+ $include: {
149
+ posts: {
150
+ where: { published: true }
151
+ }
152
+ }
153
+ });
154
+ ```
155
+
156
+ ### GlobalExceptionFilter
157
+
158
+ Register globally in `app.module.ts`:
159
+
160
+ ```typescript
161
+ import { GlobalExceptionFilter } from '@nest-extended/prisma';
162
+ import { APP_FILTER } from '@nestjs/core';
163
+
164
+ providers: [
165
+ { provide: APP_FILTER, useClass: GlobalExceptionFilter },
166
+ ]
167
+ ```
168
+
169
+ ### PrismaService Setup
170
+
171
+ Create a `PrismaService` wrapper in your project:
172
+
173
+ ```typescript
174
+ // src/prisma/prisma.service.ts
175
+ import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
176
+ import { PrismaClient } from '@prisma/client';
177
+
178
+ @Injectable()
179
+ export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
180
+ async onModuleInit() {
181
+ await this.$connect();
182
+ }
183
+ async onModuleDestroy() {
184
+ await this.$disconnect();
185
+ }
186
+ }
187
+
188
+ // src/prisma/prisma.module.ts
189
+ import { Global, Module } from '@nestjs/common';
190
+ import { PrismaService } from './prisma.service';
191
+
192
+ @Global()
193
+ @Module({
194
+ providers: [PrismaService],
195
+ exports: [PrismaService],
196
+ })
197
+ export class PrismaModule {}
198
+ ```
199
+
200
+ > **Tip**: The CLI command `nest-cli g app` and `nest-cli g service` will generate these files automatically when you select a Prisma-based database (PostgreSQL, MySQL, or SQLite).
201
+
202
+ ## Supported Databases
203
+
204
+ | Database | `$iLike` Support | Connection URL Example |
205
+ |---|---|---|
206
+ | PostgreSQL | ✅ Full support | `postgresql://user:password@localhost:5432/mydb` |
207
+ | MySQL | ⚠️ Case-insensitive by default (collation) | `mysql://user:password@localhost:3306/mydb` |
208
+ | SQLite | ❌ No case-insensitive mode | `file:./dev.db` |
209
+
210
+ ## Exported API
211
+
212
+ | Export | Type | Description |
213
+ |---|---|---|
214
+ | `NestService` | Class | Generic CRUD service with pagination & soft delete |
215
+ | `applyFilters` | Function | Apply filters/pagination to Prisma query options |
216
+ | `rawQuery` | Function | Convert FeathersJS-style query to Prisma where clause |
217
+ | `assignFilters` | Function | Extract filter params from query |
218
+ | `filterQuery` | Function | Full query parsing with operator validation |
219
+ | `cleanQuery` | Function | Validate query operators |
220
+ | `FILTERS` | Object | Filter converter definitions |
221
+ | `OPERATORS` | Array | Valid operator list |
222
+ | `GlobalExceptionFilter` | Filter | Catch-all exception handler |
223
+ | `handlePrismaError` | Function | Prisma error code translator |
224
+ | `PrismaFilters` | Interface | Filter type definition |
225
+ | `PrismaFilterOptions` | Interface | Options type definition |
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@nest-extended/prisma",
3
+ "version": "0.0.2-beta-15",
4
+ "private": false,
5
+ "type": "commonjs",
6
+ "contributors": [
7
+ {
8
+ "name": "Soubhik Kumar Gon",
9
+ "email": "soubhikgon2004@gmail.com",
10
+ "url": "https://github.com/zakhaev26"
11
+ },
12
+ {
13
+ "name": "Santanu Prasad Sahoo",
14
+ "email": "sahoosantanu92@gmail.com",
15
+ "url": "https://github.com/santanup"
16
+ }
17
+ ],
18
+ "main": "./src/index.js",
19
+ "types": "./src/index.d.ts",
20
+ "dependencies": {
21
+ "tslib": "^2.3.0",
22
+ "@nest-extended/core": "0.0.2-beta-15",
23
+ "zod": "^3.22.4"
24
+ }
25
+ }
@@ -0,0 +1,13 @@
1
+ import { PrismaFilters, PrismaFilterOptions } from '../types/PrismaFilters';
2
+ /**
3
+ * Applies filter parameters ($select, $include, $sort, $limit, $skip)
4
+ * to a Prisma query options object.
5
+ *
6
+ * This is the Prisma equivalent of the Mongoose `nestify()` function.
7
+ *
8
+ * @param queryOptions - The Prisma findMany/findFirst options object to modify
9
+ * @param filters - Extracted filter parameters
10
+ * @param options - Default pagination options
11
+ * @param isSingleOperation - If true, skip pagination (limit/skip)
12
+ */
13
+ export declare function applyFilters(queryOptions: Record<string, any>, filters: PrismaFilters, options: PrismaFilterOptions, isSingleOperation?: boolean): void;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.applyFilters = applyFilters;
4
+ /**
5
+ * Applies filter parameters ($select, $include, $sort, $limit, $skip)
6
+ * to a Prisma query options object.
7
+ *
8
+ * This is the Prisma equivalent of the Mongoose `nestify()` function.
9
+ *
10
+ * @param queryOptions - The Prisma findMany/findFirst options object to modify
11
+ * @param filters - Extracted filter parameters
12
+ * @param options - Default pagination options
13
+ * @param isSingleOperation - If true, skip pagination (limit/skip)
14
+ */
15
+ function applyFilters(queryOptions, filters, options, isSingleOperation = false) {
16
+ // Apply $select
17
+ if (filters.$select) {
18
+ if (Array.isArray(filters.$select)) {
19
+ const selectFields = filters.$select.reduce((res, key) => {
20
+ res[key] = true;
21
+ return res;
22
+ }, {});
23
+ queryOptions['select'] = selectFields;
24
+ }
25
+ else if (typeof filters.$select === 'object') {
26
+ queryOptions['select'] = filters.$select;
27
+ }
28
+ }
29
+ // Apply $include (Prisma relations — replaces Mongoose $populate)
30
+ if (filters.$include && options.defaultPagination) {
31
+ // If both $select and $include are provided, Prisma doesn't allow both.
32
+ // In that case, move included relations into $select.
33
+ if (queryOptions['select']) {
34
+ const include = filters.$include;
35
+ for (const key in include) {
36
+ if (include.hasOwnProperty(key)) {
37
+ queryOptions['select'][key] = include[key];
38
+ }
39
+ }
40
+ }
41
+ else {
42
+ queryOptions['include'] = filters.$include;
43
+ }
44
+ }
45
+ // Apply $sort → orderBy
46
+ if (filters.$sort) {
47
+ const sort = filters.$sort;
48
+ // Convert to Prisma orderBy format: [{ field: 'asc' }, { field2: 'desc' }]
49
+ const orderBy = Object.keys(sort).map(key => {
50
+ const val = sort[key];
51
+ const direction = val === -1 || val === '-1' || val === 'desc' ? 'desc' : 'asc';
52
+ return { [key]: direction };
53
+ });
54
+ queryOptions['orderBy'] = orderBy.length === 1 ? orderBy[0] : orderBy;
55
+ }
56
+ // Apply pagination: $limit and $skip
57
+ if (!isSingleOperation) {
58
+ const limit = Number(filters.$limit) || options.defaultLimit;
59
+ if (limit > 0) {
60
+ queryOptions['take'] = limit;
61
+ }
62
+ const skip = Number(filters.$skip) || options.defaultSkip;
63
+ if (skip > 0) {
64
+ queryOptions['skip'] = skip;
65
+ }
66
+ }
67
+ }
68
+ //# sourceMappingURL=apply-filters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-filters.js","sourceRoot":"","sources":["../../../../../packages/prisma/src/common/apply-filters.ts"],"names":[],"mappings":";;AAaA,oCA+DC;AA1ED;;;;;;;;;;GAUG;AACH,SAAgB,YAAY,CACxB,YAAiC,EACjC,OAAsB,EACtB,OAA4B,EAC5B,oBAA6B,KAAK;IAGlC,gBAAgB;IAChB,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QAClB,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACjC,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CACvC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;gBACT,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;gBAChB,OAAO,GAAG,CAAC;YACf,CAAC,EACD,EAAE,CACL,CAAC;YACF,YAAY,CAAC,QAAQ,CAAC,GAAG,YAAY,CAAC;QAC1C,CAAC;aAAM,IAAI,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;YAC7C,YAAY,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;QAC7C,CAAC;IACL,CAAC;IAED,kEAAkE;IAClE,IAAI,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;QAChD,wEAAwE;QACxE,sDAAsD;QACtD,IAAI,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzB,MAAM,OAAO,GAAG,OAAO,CAAC,QAA4C,CAAC;YACrE,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;gBACxB,IAAI,OAAO,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC;oBAC9B,YAAY,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;gBAC/C,CAAC;YACL,CAAC;QACL,CAAC;aAAM,CAAC;YACJ,YAAY,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC;QAC/C,CAAC;IACL,CAAC;IAED,wBAAwB;IACxB,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAChB,MAAM,IAAI,GAAG,OAAO,CAAC,KAA4B,CAAC;QAClD,2EAA2E;QAC3E,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;YACxC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;YACtB,MAAM,SAAS,GAAG,GAAG,KAAK,CAAC,CAAC,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC;YAChF,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,CAAC;QAChC,CAAC,CAAC,CAAC;QACH,YAAY,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IAC1E,CAAC;IAED,qCAAqC;IACrC,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACrB,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,YAAY,CAAC;QAC7D,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACZ,YAAY,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC;QACjC,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,WAAW,CAAC;QAC1D,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;YACX,YAAY,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;QAChC,CAAC;IACL,CAAC;AACL,CAAC"}
@@ -0,0 +1,38 @@
1
+ export declare const FILTERS: {
2
+ $sort: (value: any) => Record<string, "asc" | "desc"> | undefined;
3
+ $limit: (value: any, options: any) => any;
4
+ $skip: (value: any) => number | undefined;
5
+ $select: (value: any) => Record<string, boolean> | undefined;
6
+ $include: (value: any) => any;
7
+ };
8
+ export declare function parse(number?: any): number | undefined;
9
+ /**
10
+ * Valid Prisma query operators (FeathersJS-style).
11
+ */
12
+ export declare const OPERATORS: string[];
13
+ /**
14
+ * Converts a FeathersJS-style query object to a Prisma `where` clause.
15
+ *
16
+ * Examples:
17
+ * rawQuery({ name: 'John' })
18
+ * → { name: 'John' }
19
+ *
20
+ * rawQuery({ age: { $gt: 18 } })
21
+ * → { age: { gt: 18 } }
22
+ *
23
+ * rawQuery({ $or: [{ name: 'John' }, { name: 'Jane' }] })
24
+ * → { OR: [{ name: 'John' }, { name: 'Jane' }] }
25
+ *
26
+ * rawQuery({ name: { $like: 'John' } })
27
+ * → { name: { contains: 'John' } }
28
+ *
29
+ * rawQuery({ name: { $iLike: 'john' } })
30
+ * → { name: { contains: 'john', mode: 'insensitive' } }
31
+ */
32
+ export declare const rawQuery: (query?: any) => Record<string, any>;
33
+ export declare const filterQuery: (query: any, options?: any) => {
34
+ filters: {};
35
+ query: {};
36
+ };
37
+ export declare const assignFilters: (object: any, query: any, filters: any, options: any) => any;
38
+ export declare const cleanQuery: (query: any, operators: any, filters: any) => any;
@@ -0,0 +1,252 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cleanQuery = exports.assignFilters = exports.filterQuery = exports.rawQuery = exports.OPERATORS = exports.FILTERS = void 0;
4
+ exports.parse = parse;
5
+ const common_1 = require("@nestjs/common");
6
+ const _ = require("lodash");
7
+ exports.FILTERS = {
8
+ $sort: (value) => convertSort(value),
9
+ $limit: (value, options) => getLimit(parse(value), options === null || options === void 0 ? void 0 : options.paginate),
10
+ $skip: (value) => parse(value),
11
+ $select: (value) => convertSelect(value),
12
+ $include: (value) => value,
13
+ };
14
+ function parse(number) {
15
+ if (typeof number !== 'undefined') {
16
+ return Math.abs(parseInt(number, 10));
17
+ }
18
+ return undefined;
19
+ }
20
+ function getLimit(limit, paginate) {
21
+ if (paginate && paginate.default) {
22
+ const lower = typeof limit === 'number' && !isNaN(limit) ? limit : paginate.default;
23
+ const upper = typeof paginate.max === 'number' ? paginate.max : Number.MAX_VALUE;
24
+ return Math.min(lower, upper);
25
+ }
26
+ return limit;
27
+ }
28
+ /**
29
+ * Converts $select to Prisma select format.
30
+ * Accepts: string[], Record<string, 1 | 0>, or string
31
+ * Returns: Record<string, boolean> for Prisma
32
+ */
33
+ function convertSelect(value) {
34
+ if (!value)
35
+ return undefined;
36
+ if (Array.isArray(value)) {
37
+ return value.reduce((acc, key) => {
38
+ acc[key] = true;
39
+ return acc;
40
+ }, {});
41
+ }
42
+ if (typeof value === 'string') {
43
+ return value
44
+ .split(/[\s,]+/)
45
+ .filter(Boolean)
46
+ .reduce((acc, key) => {
47
+ acc[key] = true;
48
+ return acc;
49
+ }, {});
50
+ }
51
+ if (typeof value === 'object') {
52
+ return Object.keys(value).reduce((acc, key) => {
53
+ acc[key] = Boolean(value[key]);
54
+ return acc;
55
+ }, {});
56
+ }
57
+ return undefined;
58
+ }
59
+ /**
60
+ * Converts sort object from FeathersJS format (1/-1) to Prisma format (asc/desc).
61
+ * Input: { createdAt: -1, name: 1 }
62
+ * Output: { createdAt: 'desc', name: 'asc' }
63
+ */
64
+ function convertSort(sort) {
65
+ if (!sort || typeof sort !== 'object' || Array.isArray(sort)) {
66
+ return undefined;
67
+ }
68
+ return Object.keys(sort).reduce((result, key) => {
69
+ const val = parseInt(sort[key], 10);
70
+ result[key] = val === -1 ? 'desc' : 'asc';
71
+ return result;
72
+ }, {});
73
+ }
74
+ /**
75
+ * Valid Prisma query operators (FeathersJS-style).
76
+ */
77
+ exports.OPERATORS = [
78
+ '$eq',
79
+ '$ne',
80
+ '$gte',
81
+ '$gt',
82
+ '$lte',
83
+ '$lt',
84
+ '$in',
85
+ '$nin',
86
+ '$like',
87
+ '$notLike',
88
+ '$iLike',
89
+ '$notILike',
90
+ '$or',
91
+ '$and',
92
+ ];
93
+ /**
94
+ * Converts a single operator expression to Prisma where clause.
95
+ */
96
+ function convertOperator(key, value) {
97
+ switch (key) {
98
+ case '$eq':
99
+ return value;
100
+ case '$ne':
101
+ return { not: value };
102
+ case '$gt':
103
+ return { gt: value };
104
+ case '$gte':
105
+ return { gte: value };
106
+ case '$lt':
107
+ return { lt: value };
108
+ case '$lte':
109
+ return { lte: value };
110
+ case '$in':
111
+ return { in: Array.isArray(value) ? value : [value] };
112
+ case '$nin':
113
+ return { notIn: Array.isArray(value) ? value : [value] };
114
+ case '$like':
115
+ return { contains: value };
116
+ case '$notLike':
117
+ return { not: { contains: value } };
118
+ case '$iLike':
119
+ return { contains: value, mode: 'insensitive' };
120
+ case '$notILike':
121
+ return { not: { contains: value, mode: 'insensitive' } };
122
+ default:
123
+ return value;
124
+ }
125
+ }
126
+ /**
127
+ * Converts a FeathersJS-style query object to a Prisma `where` clause.
128
+ *
129
+ * Examples:
130
+ * rawQuery({ name: 'John' })
131
+ * → { name: 'John' }
132
+ *
133
+ * rawQuery({ age: { $gt: 18 } })
134
+ * → { age: { gt: 18 } }
135
+ *
136
+ * rawQuery({ $or: [{ name: 'John' }, { name: 'Jane' }] })
137
+ * → { OR: [{ name: 'John' }, { name: 'Jane' }] }
138
+ *
139
+ * rawQuery({ name: { $like: 'John' } })
140
+ * → { name: { contains: 'John' } }
141
+ *
142
+ * rawQuery({ name: { $iLike: 'john' } })
143
+ * → { name: { contains: 'john', mode: 'insensitive' } }
144
+ */
145
+ const rawQuery = (query = {}) => {
146
+ const where = {};
147
+ for (const key in query) {
148
+ if (!query.hasOwnProperty(key))
149
+ continue;
150
+ if (key === '$or' && Array.isArray(query[key])) {
151
+ where['OR'] = query[key].map((subQuery) => (0, exports.rawQuery)(subQuery));
152
+ }
153
+ else if (key === '$and' && Array.isArray(query[key])) {
154
+ where['AND'] = query[key].map((subQuery) => (0, exports.rawQuery)(subQuery));
155
+ }
156
+ else if (key.startsWith('$')) {
157
+ // Skip filter keys — they are handled separately
158
+ continue;
159
+ }
160
+ else if (typeof query[key] === 'object' && query[key] !== null && !Array.isArray(query[key])) {
161
+ // Field-level operators: { age: { $gt: 18, $lt: 65 } }
162
+ const fieldConditions = {};
163
+ let hasOperators = false;
164
+ for (const opKey in query[key]) {
165
+ if (opKey.startsWith('$')) {
166
+ hasOperators = true;
167
+ const converted = convertOperator(opKey, query[key][opKey]);
168
+ if (typeof converted === 'object' && converted !== null && !Array.isArray(converted)) {
169
+ // Merge operator result into field conditions
170
+ // Handle $notLike / $notILike which produce { not: { contains: ... } }
171
+ Object.assign(fieldConditions, converted);
172
+ }
173
+ else {
174
+ // Simple value replacement (e.g., $eq returns raw value)
175
+ where[key] = converted;
176
+ }
177
+ }
178
+ }
179
+ if (hasOperators && Object.keys(fieldConditions).length > 0) {
180
+ where[key] = fieldConditions;
181
+ }
182
+ else if (!hasOperators) {
183
+ // Plain nested object (not operators)
184
+ where[key] = query[key];
185
+ }
186
+ }
187
+ else {
188
+ // Direct equality
189
+ where[key] = query[key];
190
+ }
191
+ }
192
+ return where;
193
+ };
194
+ exports.rawQuery = rawQuery;
195
+ const filterQuery = (query, options = {}) => {
196
+ const {
197
+ // @ts-ignore
198
+ filters: additionalFilters = {},
199
+ // @ts-ignore
200
+ operators: additionalOperators = [], } = options;
201
+ const result = {
202
+ filters: {},
203
+ query: {},
204
+ };
205
+ result.filters = (0, exports.assignFilters)({}, query, exports.FILTERS, options);
206
+ result.filters = (0, exports.assignFilters)(result.filters, query, additionalFilters, options);
207
+ result.query = (0, exports.cleanQuery)(query, exports.OPERATORS.concat(additionalOperators), result.filters);
208
+ return result;
209
+ };
210
+ exports.filterQuery = filterQuery;
211
+ const assignFilters = (object, query, filters, options) => {
212
+ if (Array.isArray(filters)) {
213
+ _.forEach(filters, (key) => {
214
+ if (query[key] !== undefined) {
215
+ object[key] = query[key];
216
+ }
217
+ });
218
+ }
219
+ else {
220
+ _.forEach(filters, (converter, key) => {
221
+ const converted = converter(query[key], options);
222
+ if (converted !== undefined) {
223
+ object[key] = converted;
224
+ }
225
+ });
226
+ }
227
+ return object;
228
+ };
229
+ exports.assignFilters = assignFilters;
230
+ const cleanQuery = (query, operators, filters) => {
231
+ if (Array.isArray(query)) {
232
+ return query.map((value) => (0, exports.cleanQuery)(value, operators, filters));
233
+ }
234
+ else if (_.isPlainObject(query)) {
235
+ const result = {};
236
+ _.forEach(query, (value, key) => {
237
+ if (key.startsWith('$')) {
238
+ if (filters[key] === undefined && !operators.includes(key)) {
239
+ throw new common_1.BadRequestException(`Invalid query parameter: ${key}`, query);
240
+ }
241
+ }
242
+ result[key] = (0, exports.cleanQuery)(value, operators, filters);
243
+ });
244
+ Object.getOwnPropertySymbols(query).forEach((symbol) => {
245
+ result[symbol] = query[symbol];
246
+ });
247
+ return result;
248
+ }
249
+ return query;
250
+ };
251
+ exports.cleanQuery = cleanQuery;
252
+ //# sourceMappingURL=query.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.utils.js","sourceRoot":"","sources":["../../../../../packages/prisma/src/common/query.utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { ArgumentsHost, ExceptionFilter } from '@nestjs/common';
2
+ export declare class GlobalExceptionFilter implements ExceptionFilter {
3
+ catch(exception: any, host: ArgumentsHost): any;
4
+ }
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GlobalExceptionFilter = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const common_1 = require("@nestjs/common");
6
+ const http_exception_1 = require("@nestjs/common/exceptions/http.exception");
7
+ const zod_1 = require("zod");
8
+ const prisma_error_filter_1 = require("./prisma-error.filter");
9
+ let GlobalExceptionFilter = class GlobalExceptionFilter {
10
+ catch(exception, host) {
11
+ var _a, _b;
12
+ const ctx = host.switchToHttp();
13
+ const response = ctx.getResponse();
14
+ const request = ctx.getRequest();
15
+ if (exception instanceof http_exception_1.HttpException) {
16
+ return response
17
+ .status(exception.getStatus())
18
+ .json(exception.getResponse());
19
+ }
20
+ // Prisma known request errors (P2002, P2003, P2025, etc.)
21
+ if (((_a = exception.constructor) === null || _a === void 0 ? void 0 : _a.name) === 'PrismaClientKnownRequestError') {
22
+ const error = new common_1.BadRequestException(exception);
23
+ return response
24
+ .status(error.getStatus())
25
+ .json((0, prisma_error_filter_1.handlePrismaError)(exception));
26
+ }
27
+ // Prisma validation errors
28
+ if (((_b = exception.constructor) === null || _b === void 0 ? void 0 : _b.name) === 'PrismaClientValidationError') {
29
+ const error = new common_1.BadRequestException(exception.message);
30
+ return response.status(error.getStatus()).json(error.getResponse());
31
+ }
32
+ if (exception instanceof zod_1.ZodError) {
33
+ const error = new common_1.BadRequestException(exception);
34
+ return response.status(error.getStatus()).json(error.getResponse());
35
+ }
36
+ response.status(500).json({
37
+ statusCode: 500,
38
+ timestamp: new Date().toISOString(),
39
+ error: {
40
+ name: exception.name,
41
+ message: exception.message || 'Internal Server Error',
42
+ // todo: remove stack in production
43
+ stack: process.env['NODE_ENV'] === 'production' ? undefined : exception.stack,
44
+ },
45
+ path: request.url,
46
+ });
47
+ }
48
+ };
49
+ exports.GlobalExceptionFilter = GlobalExceptionFilter;
50
+ exports.GlobalExceptionFilter = GlobalExceptionFilter = tslib_1.__decorate([
51
+ (0, common_1.Catch)()
52
+ ], GlobalExceptionFilter);
53
+ //# sourceMappingURL=global-exception.filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"global-exception.filter.js","sourceRoot":"","sources":["../../../../../packages/prisma/src/filters/global-exception.filter.ts"],"names":[],"mappings":";;;;AAAA,2CAKwB;AACxB,6EAAyE;AACzE,6BAA+B;AAC/B,+DAA0D;AAGnD,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IAC9B,KAAK,CAAC,SAAc,EAAE,IAAmB;;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;QACnC,MAAM,OAAO,GAAG,GAAG,CAAC,UAAU,EAAE,CAAC;QAEjC,IAAI,SAAS,YAAY,8BAAa,EAAE,CAAC;YACrC,OAAO,QAAQ;iBACV,MAAM,CAAC,SAAS,CAAC,SAAS,EAAE,CAAC;iBAC7B,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC;QACvC,CAAC;QAED,0DAA0D;QAC1D,IAAI,CAAA,MAAA,SAAS,CAAC,WAAW,0CAAE,IAAI,MAAK,+BAA+B,EAAE,CAAC;YAClE,MAAM,KAAK,GAAG,IAAI,4BAAmB,CAAC,SAAS,CAAC,CAAC;YACjD,OAAO,QAAQ;iBACV,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;iBACzB,IAAI,CAAC,IAAA,uCAAiB,EAAC,SAAS,CAAC,CAAC,CAAC;QAC5C,CAAC;QAED,2BAA2B;QAC3B,IAAI,CAAA,MAAA,SAAS,CAAC,WAAW,0CAAE,IAAI,MAAK,6BAA6B,EAAE,CAAC;YAChE,MAAM,KAAK,GAAG,IAAI,4BAAmB,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;YACzD,OAAO,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;QACxE,CAAC;QAED,IAAI,SAAS,YAAY,cAAQ,EAAE,CAAC;YAChC,MAAM,KAAK,GAAG,IAAI,4BAAmB,CAAC,SAAS,CAAC,CAAC;YACjD,OAAO,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;QACxE,CAAC;QAED,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YACtB,UAAU,EAAE,GAAG;YACf,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,KAAK,EAAE;gBACH,IAAI,EAAE,SAAS,CAAC,IAAI;gBACpB,OAAO,EAAE,SAAS,CAAC,OAAO,IAAI,uBAAuB;gBACrD,mCAAmC;gBACnC,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK;aAChF;YACD,IAAI,EAAE,OAAO,CAAC,GAAG;SACpB,CAAC,CAAC;IACP,CAAC;CACJ,CAAA;AA3CY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,cAAK,GAAE;GACK,qBAAqB,CA2CjC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Translates Prisma error codes to human-readable messages.
3
+ * @see https://www.prisma.io/docs/reference/api-reference/error-reference
4
+ */
5
+ export declare function handlePrismaError(exception: any): {
6
+ message: string;
7
+ details?: string;
8
+ };
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handlePrismaError = handlePrismaError;
4
+ /**
5
+ * Translates Prisma error codes to human-readable messages.
6
+ * @see https://www.prisma.io/docs/reference/api-reference/error-reference
7
+ */
8
+ function handlePrismaError(exception) {
9
+ var _a, _b, _c, _d, _e, _f, _g, _h;
10
+ let message = 'A database error occurred.';
11
+ const details = exception.message;
12
+ switch (exception.code) {
13
+ case 'P2002': {
14
+ const target = (_a = exception.meta) === null || _a === void 0 ? void 0 : _a.target;
15
+ const field = Array.isArray(target) ? target.join(', ') : target || 'unknown field';
16
+ message = `${field} must be unique. A record with this value already exists.`;
17
+ break;
18
+ }
19
+ case 'P2003': {
20
+ const fieldName = ((_b = exception.meta) === null || _b === void 0 ? void 0 : _b.field_name) || 'unknown field';
21
+ message = `Foreign key constraint failed on field: ${fieldName}.`;
22
+ break;
23
+ }
24
+ case 'P2025':
25
+ message = 'Record not found. The requested resource does not exist or has been deleted.';
26
+ break;
27
+ case 'P2014': {
28
+ const relationName = ((_c = exception.meta) === null || _c === void 0 ? void 0 : _c.relation_name) || 'unknown';
29
+ message = `The change you are trying to make would violate the required relation '${relationName}'.`;
30
+ break;
31
+ }
32
+ case 'P2000': {
33
+ const column = ((_d = exception.meta) === null || _d === void 0 ? void 0 : _d.column_name) || 'unknown column';
34
+ message = `The provided value is too long for column '${column}'.`;
35
+ break;
36
+ }
37
+ case 'P2006': {
38
+ const fieldMeta = ((_e = exception.meta) === null || _e === void 0 ? void 0 : _e.field_name) || 'unknown field';
39
+ message = `The provided value for '${fieldMeta}' is invalid.`;
40
+ break;
41
+ }
42
+ case 'P2011': {
43
+ const constraint = ((_f = exception.meta) === null || _f === void 0 ? void 0 : _f.constraint) || 'unknown field';
44
+ message = `Null constraint violation on '${constraint}'. This field cannot be null.`;
45
+ break;
46
+ }
47
+ case 'P2024':
48
+ message = 'Connection pool timeout. The database connection could not be established in time. Please try again later.';
49
+ break;
50
+ case 'P2021': {
51
+ const table = ((_g = exception.meta) === null || _g === void 0 ? void 0 : _g.table) || 'unknown';
52
+ message = `The table '${table}' does not exist in the current database.`;
53
+ break;
54
+ }
55
+ case 'P2022': {
56
+ const col = ((_h = exception.meta) === null || _h === void 0 ? void 0 : _h.column) || 'unknown';
57
+ message = `The column '${col}' does not exist in the current database.`;
58
+ break;
59
+ }
60
+ default:
61
+ message =
62
+ 'An unknown database error occurred. Please contact support if the issue persists.';
63
+ }
64
+ return {
65
+ message,
66
+ details: process.env['NODE_ENV'] === 'production' ? undefined : details,
67
+ };
68
+ }
69
+ //# sourceMappingURL=prisma-error.filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prisma-error.filter.js","sourceRoot":"","sources":["../../../../../packages/prisma/src/filters/prisma-error.filter.ts"],"names":[],"mappings":";;AAIA,8CA0EC;AA9ED;;;GAGG;AACH,SAAgB,iBAAiB,CAAC,SAAc;;IAI5C,IAAI,OAAO,GAAG,4BAA4B,CAAC;IAC3C,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC;IAElC,QAAQ,SAAS,CAAC,IAAI,EAAE,CAAC;QACrB,KAAK,OAAO,CAAC,CAAC,CAAC;YACX,MAAM,MAAM,GAAG,MAAA,SAAS,CAAC,IAAI,0CAAE,MAAM,CAAC;YACtC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,eAAe,CAAC;YACpF,OAAO,GAAG,GAAG,KAAK,2DAA2D,CAAC;YAC9E,MAAM;QACV,CAAC;QAED,KAAK,OAAO,CAAC,CAAC,CAAC;YACX,MAAM,SAAS,GAAG,CAAA,MAAA,SAAS,CAAC,IAAI,0CAAE,UAAU,KAAI,eAAe,CAAC;YAChE,OAAO,GAAG,2CAA2C,SAAS,GAAG,CAAC;YAClE,MAAM;QACV,CAAC;QAED,KAAK,OAAO;YACR,OAAO,GAAG,8EAA8E,CAAC;YACzF,MAAM;QAEV,KAAK,OAAO,CAAC,CAAC,CAAC;YACX,MAAM,YAAY,GAAG,CAAA,MAAA,SAAS,CAAC,IAAI,0CAAE,aAAa,KAAI,SAAS,CAAC;YAChE,OAAO,GAAG,0EAA0E,YAAY,IAAI,CAAC;YACrG,MAAM;QACV,CAAC;QAED,KAAK,OAAO,CAAC,CAAC,CAAC;YACX,MAAM,MAAM,GAAG,CAAA,MAAA,SAAS,CAAC,IAAI,0CAAE,WAAW,KAAI,gBAAgB,CAAC;YAC/D,OAAO,GAAG,8CAA8C,MAAM,IAAI,CAAC;YACnE,MAAM;QACV,CAAC;QAED,KAAK,OAAO,CAAC,CAAC,CAAC;YACX,MAAM,SAAS,GAAG,CAAA,MAAA,SAAS,CAAC,IAAI,0CAAE,UAAU,KAAI,eAAe,CAAC;YAChE,OAAO,GAAG,2BAA2B,SAAS,eAAe,CAAC;YAC9D,MAAM;QACV,CAAC;QAED,KAAK,OAAO,CAAC,CAAC,CAAC;YACX,MAAM,UAAU,GAAG,CAAA,MAAA,SAAS,CAAC,IAAI,0CAAE,UAAU,KAAI,eAAe,CAAC;YACjE,OAAO,GAAG,iCAAiC,UAAU,+BAA+B,CAAC;YACrF,MAAM;QACV,CAAC;QAED,KAAK,OAAO;YACR,OAAO,GAAG,4GAA4G,CAAC;YACvH,MAAM;QAEV,KAAK,OAAO,CAAC,CAAC,CAAC;YACX,MAAM,KAAK,GAAG,CAAA,MAAA,SAAS,CAAC,IAAI,0CAAE,KAAK,KAAI,SAAS,CAAC;YACjD,OAAO,GAAG,cAAc,KAAK,2CAA2C,CAAC;YACzE,MAAM;QACV,CAAC;QAED,KAAK,OAAO,CAAC,CAAC,CAAC;YACX,MAAM,GAAG,GAAG,CAAA,MAAA,SAAS,CAAC,IAAI,0CAAE,MAAM,KAAI,SAAS,CAAC;YAChD,OAAO,GAAG,eAAe,GAAG,2CAA2C,CAAC;YACxE,MAAM;QACV,CAAC;QAED;YACI,OAAO;gBACH,mFAAmF,CAAC;IAChG,CAAC;IAED,OAAO;QACH,OAAO;QACP,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;KAC1E,CAAC;AACN,CAAC"}
package/src/index.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ export * from './lib/nest.service';
2
+ export * from './common/query.utils';
3
+ export * from './common/apply-filters';
4
+ export * from './types/PrismaFilters';
5
+ export * from './filters/global-exception.filter';
6
+ export * from './filters/prisma-error.filter';
package/src/index.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./lib/nest.service"), exports);
5
+ tslib_1.__exportStar(require("./common/query.utils"), exports);
6
+ tslib_1.__exportStar(require("./common/apply-filters"), exports);
7
+ tslib_1.__exportStar(require("./types/PrismaFilters"), exports);
8
+ tslib_1.__exportStar(require("./filters/global-exception.filter"), exports);
9
+ tslib_1.__exportStar(require("./filters/prisma-error.filter"), exports);
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/prisma/src/index.ts"],"names":[],"mappings":";;;AAAA,6DAAmC;AACnC,+DAAqC;AACrC,iEAAuC;AACvC,gEAAsC;AACtC,4EAAkD;AAClD,wEAA8C"}
@@ -0,0 +1,45 @@
1
+ import { PaginatedResponse } from '@nest-extended/core';
2
+ import { NestServiceOptions } from '@nest-extended/core';
3
+ import { SoftDeleteConfig } from '@nest-extended/core';
4
+ /**
5
+ * Generic CRUD service for Prisma — same API surface as the Mongoose NestService.
6
+ *
7
+ * Usage:
8
+ * ```typescript
9
+ * @Injectable()
10
+ * export class CatsService extends NestService<any> {
11
+ * constructor(private readonly prisma: PrismaService) {
12
+ * super(prisma.cat);
13
+ * }
14
+ * }
15
+ * ```
16
+ *
17
+ * Supports:
18
+ * - Full CRUD: _find, _get, _create, _patch, _remove
19
+ * - FeathersJS-style query operators: $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $like, $notLike, $iLike, $notILike, $or, $and
20
+ * - Pagination: $limit, $skip
21
+ * - Field selection: $select
22
+ * - Relations: $include (replaces Mongoose $populate)
23
+ * - Sorting: $sort
24
+ * - Soft delete: configurable via SoftDeleteConfig
25
+ * - Bulk operations: multi mode for createMany/updateMany
26
+ */
27
+ export declare class NestService<T> {
28
+ private model;
29
+ private options;
30
+ private softDeleteConfig;
31
+ constructor(model: any, serviceOptions?: NestServiceOptions, softDeleteConfig?: SoftDeleteConfig);
32
+ /**
33
+ * Merge soft-delete filter into the where clause if soft delete is enabled.
34
+ */
35
+ private applySoftDeleteFilter;
36
+ _find<P extends boolean = true>(query?: Record<string, any>, findOptions?: {
37
+ pagination?: P;
38
+ }): Promise<P extends true ? PaginatedResponse<T> : T[]>;
39
+ _create(data: Partial<T>): Promise<T>;
40
+ _create(data: Partial<T>[]): Promise<T[]>;
41
+ _patch(id: string | null, data: Record<any, any>, query?: Record<string, any>): Promise<T | T[] | null>;
42
+ _get(id: string, query?: Record<string, any>): Promise<T | null>;
43
+ _remove(id: string | null, query?: Record<string, any>, user?: any): Promise<T | T[] | null>;
44
+ getCount(filter?: Record<string, any>): Promise<any>;
45
+ }
@@ -0,0 +1,216 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NestService = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const common_1 = require("@nestjs/common");
6
+ const query_utils_1 = require("../common/query.utils");
7
+ const core_1 = require("@nest-extended/core");
8
+ const apply_filters_1 = require("../common/apply-filters");
9
+ const core_2 = require("@nest-extended/core");
10
+ /**
11
+ * Default soft delete configuration for Prisma.
12
+ * Uses `deleted` boolean field instead of MongoDB-style `$ne` query.
13
+ */
14
+ const defaultSoftDeleteConfig = {
15
+ getQuery: () => ({ [core_1.options.deleteKey || 'deleted']: { not: true } }),
16
+ getData: (user) => {
17
+ var _a;
18
+ return ({
19
+ deleted: true,
20
+ deletedBy: (_a = user === null || user === void 0 ? void 0 : user.id) !== null && _a !== void 0 ? _a : null,
21
+ deletedAt: new Date(),
22
+ });
23
+ },
24
+ };
25
+ /**
26
+ * Generic CRUD service for Prisma — same API surface as the Mongoose NestService.
27
+ *
28
+ * Usage:
29
+ * ```typescript
30
+ * @Injectable()
31
+ * export class CatsService extends NestService<any> {
32
+ * constructor(private readonly prisma: PrismaService) {
33
+ * super(prisma.cat);
34
+ * }
35
+ * }
36
+ * ```
37
+ *
38
+ * Supports:
39
+ * - Full CRUD: _find, _get, _create, _patch, _remove
40
+ * - FeathersJS-style query operators: $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $like, $notLike, $iLike, $notILike, $or, $and
41
+ * - Pagination: $limit, $skip
42
+ * - Field selection: $select
43
+ * - Relations: $include (replaces Mongoose $populate)
44
+ * - Sorting: $sort
45
+ * - Soft delete: configurable via SoftDeleteConfig
46
+ * - Bulk operations: multi mode for createMany/updateMany
47
+ */
48
+ class NestService {
49
+ constructor(model, serviceOptions = {}, softDeleteConfig) {
50
+ this.model = model;
51
+ this.options = Object.assign({ multi: false, softDelete: true, pagination: true }, serviceOptions);
52
+ this.softDeleteConfig = softDeleteConfig || defaultSoftDeleteConfig;
53
+ }
54
+ /**
55
+ * Merge soft-delete filter into the where clause if soft delete is enabled.
56
+ */
57
+ applySoftDeleteFilter(where) {
58
+ if (this.options.softDelete) {
59
+ const softDeleteQuery = this.softDeleteConfig.getQuery();
60
+ // Convert NestExtended soft delete query to Prisma format
61
+ // { deleted: { $ne: true } } → { deleted: { not: true } }
62
+ for (const key in softDeleteQuery) {
63
+ if (softDeleteQuery.hasOwnProperty(key)) {
64
+ const val = softDeleteQuery[key];
65
+ if (typeof val === 'object' && val !== null && val['$ne'] !== undefined) {
66
+ where[key] = { not: val['$ne'] };
67
+ }
68
+ else if (typeof val === 'object' && val !== null && val['not'] !== undefined) {
69
+ where[key] = val;
70
+ }
71
+ else {
72
+ where[key] = val;
73
+ }
74
+ }
75
+ }
76
+ }
77
+ }
78
+ _find() {
79
+ return tslib_1.__awaiter(this, arguments, void 0, function* (query = {}, findOptions = {
80
+ pagination: this.options.pagination,
81
+ }) {
82
+ var _a;
83
+ query = Object.assign({}, query);
84
+ const filters = (0, query_utils_1.assignFilters)({}, query, query_utils_1.FILTERS, {});
85
+ const where = (0, query_utils_1.rawQuery)(query);
86
+ // Apply soft delete filter
87
+ this.applySoftDeleteFilter(where);
88
+ const isPaginationEnabled = (_a = findOptions.pagination) !== null && _a !== void 0 ? _a : this.options.pagination;
89
+ // Build Prisma query options
90
+ const queryOptions = { where };
91
+ (0, apply_filters_1.applyFilters)(queryOptions, filters, core_1.options, !isPaginationEnabled);
92
+ if (!isPaginationEnabled) {
93
+ return (yield this.model.findMany(queryOptions));
94
+ }
95
+ const [data, total] = yield Promise.all([
96
+ this.model.findMany(queryOptions),
97
+ this.model.count({ where }),
98
+ ]);
99
+ return {
100
+ total,
101
+ $limit: Number(filters.$limit) || core_1.options.defaultLimit,
102
+ $skip: Number(filters.$skip) || core_1.options.defaultSkip,
103
+ data,
104
+ };
105
+ });
106
+ }
107
+ _create(data) {
108
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
109
+ const multi = this.options.multi;
110
+ if (multi) {
111
+ if (Array.isArray(data)) {
112
+ // Prisma createMany doesn't return the created records
113
+ // We need to use a transaction for bulk create with return
114
+ const results = [];
115
+ for (const item of data) {
116
+ const created = yield this.model.create({ data: item });
117
+ results.push(created);
118
+ }
119
+ return results;
120
+ }
121
+ return this.model.create({ data });
122
+ }
123
+ // When multi is disabled, only accept single object
124
+ if (Array.isArray(data)) {
125
+ throw new common_1.BadRequestException('Bulk creation is not enabled. Set multi: true in service options to allow array input.');
126
+ }
127
+ return this.model.create({ data });
128
+ });
129
+ }
130
+ _patch(id_1, data_1) {
131
+ return tslib_1.__awaiter(this, arguments, void 0, function* (id, data, query = {}) {
132
+ query = Object.assign({}, query);
133
+ const where = (0, query_utils_1.rawQuery)(query);
134
+ this.applySoftDeleteFilter(where);
135
+ if (id) {
136
+ // Single record update by ID
137
+ const searchWhere = Object.assign({ id }, where);
138
+ const filters = (0, query_utils_1.assignFilters)({}, query, query_utils_1.FILTERS, {});
139
+ const queryOptions = {};
140
+ // Apply select/include if provided
141
+ if (filters.$select) {
142
+ queryOptions['select'] = filters.$select;
143
+ }
144
+ if (filters.$include) {
145
+ if (queryOptions['select']) {
146
+ const include = filters.$include;
147
+ for (const key in include) {
148
+ if (include.hasOwnProperty(key)) {
149
+ queryOptions['select'][key] = include[key];
150
+ }
151
+ }
152
+ }
153
+ else {
154
+ queryOptions['include'] = filters.$include;
155
+ }
156
+ }
157
+ return this.model.update(Object.assign({ where: searchWhere, data }, queryOptions));
158
+ }
159
+ // Bulk update (id is null)
160
+ const result = yield this.model.updateMany({
161
+ where,
162
+ data,
163
+ });
164
+ if (result.count > 0) {
165
+ return this.model.findMany({ where });
166
+ }
167
+ return [];
168
+ });
169
+ }
170
+ _get(id_1) {
171
+ return tslib_1.__awaiter(this, arguments, void 0, function* (id, query = {}) {
172
+ query = Object.assign({}, query);
173
+ const filters = (0, query_utils_1.assignFilters)({}, query, query_utils_1.FILTERS, {});
174
+ const where = (0, query_utils_1.rawQuery)(query);
175
+ this.applySoftDeleteFilter(where);
176
+ const searchWhere = Object.assign(Object.assign({}, where), { id });
177
+ const queryOptions = { where: searchWhere };
178
+ const isSingleOperation = true;
179
+ (0, apply_filters_1.applyFilters)(queryOptions, filters, core_1.options, isSingleOperation);
180
+ // Remove take/skip for single record fetch
181
+ delete queryOptions['take'];
182
+ delete queryOptions['skip'];
183
+ return (yield this.model.findFirst(queryOptions)) || null;
184
+ });
185
+ }
186
+ _remove(id_1) {
187
+ return tslib_1.__awaiter(this, arguments, void 0, function* (id, query = {}, user) {
188
+ query = Object.assign({}, query);
189
+ const where = (0, query_utils_1.rawQuery)(query);
190
+ const data = id ? yield this._get(id, query) : null;
191
+ if (this.options.softDelete) {
192
+ // Get user from parameter or fallback to CLS context
193
+ const currentUser = user !== null && user !== void 0 ? user : (0, core_2.getCurrentUser)();
194
+ // Soft delete: mark as deleted using configured getData
195
+ const softDeleteData = this.softDeleteConfig.getData(currentUser);
196
+ yield this._patch(id, softDeleteData, where);
197
+ return data;
198
+ }
199
+ // Hard delete: actually remove from database
200
+ if (id) {
201
+ yield this.model.delete({ where: Object.assign({ id }, where) });
202
+ }
203
+ else {
204
+ yield this.model.deleteMany({ where });
205
+ }
206
+ return data;
207
+ });
208
+ }
209
+ getCount() {
210
+ return tslib_1.__awaiter(this, arguments, void 0, function* (filter = {}) {
211
+ return this.model.count({ where: filter });
212
+ });
213
+ }
214
+ }
215
+ exports.NestService = NestService;
216
+ //# sourceMappingURL=nest.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nest.service.js","sourceRoot":"","sources":["../../../../../packages/prisma/src/lib/nest.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export interface PrismaFilterOptions {
2
+ defaultLimit: number;
3
+ defaultSkip: number;
4
+ defaultPagination: boolean;
5
+ }
6
+ export interface PrismaFilters {
7
+ $select?: Record<string, boolean> | string[];
8
+ $include?: Record<string, boolean | object>;
9
+ $sort?: Record<string, 'asc' | 'desc'> | Record<string, number>;
10
+ $limit?: number | string;
11
+ $skip?: number | string;
12
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=PrismaFilters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PrismaFilters.js","sourceRoot":"","sources":["../../../../../packages/prisma/src/types/PrismaFilters.ts"],"names":[],"mappings":""}