@lenne.tech/nest-server 11.21.3 → 11.22.1
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/.claude/rules/architecture.md +79 -0
- package/.claude/rules/better-auth.md +262 -0
- package/.claude/rules/configurable-features.md +308 -0
- package/.claude/rules/core-modules.md +205 -0
- package/.claude/rules/framework-compatibility.md +79 -0
- package/.claude/rules/migration-guides.md +149 -0
- package/.claude/rules/module-deprecation.md +214 -0
- package/.claude/rules/module-inheritance.md +97 -0
- package/.claude/rules/package-management.md +112 -0
- package/.claude/rules/role-system.md +146 -0
- package/.claude/rules/testing.md +120 -0
- package/.claude/rules/versioning.md +53 -0
- package/CLAUDE.md +174 -0
- package/FRAMEWORK-API.md +231 -0
- package/dist/core/common/interfaces/server-options.interface.d.ts +10 -0
- package/dist/core/modules/error-code/error-code.module.js.map +1 -1
- package/dist/core.module.d.ts +3 -3
- package/dist/core.module.js +17 -4
- package/dist/core.module.js.map +1 -1
- package/dist/server/modules/file/file-info.model.d.ts +1 -5
- package/dist/server/modules/user/user.model.d.ts +1 -5
- package/dist/server/server.module.js +6 -6
- package/dist/server/server.module.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/docs/REQUEST-LIFECYCLE.md +1256 -0
- package/docs/error-codes.md +446 -0
- package/migration-guides/11.10.x-to-11.11.x.md +266 -0
- package/migration-guides/11.11.x-to-11.12.x.md +323 -0
- package/migration-guides/11.12.x-to-11.13.0.md +612 -0
- package/migration-guides/11.13.x-to-11.14.0.md +348 -0
- package/migration-guides/11.14.x-to-11.15.0.md +262 -0
- package/migration-guides/11.15.0-to-11.15.3.md +118 -0
- package/migration-guides/11.15.x-to-11.16.0.md +497 -0
- package/migration-guides/11.16.x-to-11.17.0.md +130 -0
- package/migration-guides/11.17.x-to-11.18.0.md +393 -0
- package/migration-guides/11.18.x-to-11.19.0.md +151 -0
- package/migration-guides/11.19.x-to-11.20.0.md +170 -0
- package/migration-guides/11.20.x-to-11.21.0.md +216 -0
- package/migration-guides/11.21.0-to-11.21.1.md +194 -0
- package/migration-guides/11.21.1-to-11.21.2.md +114 -0
- package/migration-guides/11.21.2-to-11.21.3.md +175 -0
- package/migration-guides/11.21.x-to-11.22.0.md +224 -0
- package/migration-guides/11.22.0-to-11.22.1.md +105 -0
- package/migration-guides/11.3.x-to-11.4.x.md +233 -0
- package/migration-guides/11.6.x-to-11.7.x.md +394 -0
- package/migration-guides/11.7.x-to-11.8.x.md +318 -0
- package/migration-guides/11.8.x-to-11.9.x.md +322 -0
- package/migration-guides/11.9.x-to-11.10.x.md +571 -0
- package/migration-guides/TEMPLATE.md +113 -0
- package/package.json +25 -18
- package/src/core/common/interfaces/server-options.interface.ts +83 -16
- package/src/core/modules/better-auth/CUSTOMIZATION.md +24 -17
- package/src/core/modules/better-auth/INTEGRATION-CHECKLIST.md +5 -5
- package/src/core/modules/error-code/INTEGRATION-CHECKLIST.md +42 -12
- package/src/core/modules/error-code/error-code.module.ts +4 -9
- package/src/core.module.ts +52 -10
- package/src/server/server.module.ts +7 -9
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
# Error Codes Documentation
|
|
2
|
+
|
|
3
|
+
This document lists all available error codes with their descriptions and solutions.
|
|
4
|
+
|
|
5
|
+
## Error Code Format
|
|
6
|
+
|
|
7
|
+
All error codes follow the format: `#PREFIX_XXXX: Description`
|
|
8
|
+
|
|
9
|
+
- **PREFIX**: Identifies the source module (e.g., LTNS for core)
|
|
10
|
+
- **XXXX**: Unique 4-digit number within the prefix
|
|
11
|
+
|
|
12
|
+
## Error Code Ranges
|
|
13
|
+
|
|
14
|
+
| Range | Category |
|
|
15
|
+
|-------|----------|
|
|
16
|
+
| LTNS_0001-LTNS_0099 | Authentication errors |
|
|
17
|
+
| LTNS_0100-LTNS_0199 | Authorization errors |
|
|
18
|
+
| LTNS_0200-LTNS_0299 | User errors |
|
|
19
|
+
| LTNS_0300-LTNS_0399 | Validation errors |
|
|
20
|
+
| LTNS_0400-LTNS_0499 | Resource errors |
|
|
21
|
+
| LTNS_0500-LTNS_0599 | File errors |
|
|
22
|
+
| LTNS_0600-LTNS_0699 | Database errors |
|
|
23
|
+
| LTNS_0700-LTNS_0799 | External service errors |
|
|
24
|
+
| LTNS_0800-LTNS_0899 | Configuration errors |
|
|
25
|
+
| LTNS_0900-LTNS_0999 | Internal errors |
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Authentication Errors (LTNS_0001-LTNS_0099)
|
|
30
|
+
|
|
31
|
+
### LTNS_0001: userNotFound
|
|
32
|
+
|
|
33
|
+
**Message:** User not found with given email
|
|
34
|
+
|
|
35
|
+
**Description:** Thrown when email lookup fails during authentication
|
|
36
|
+
|
|
37
|
+
**Solution:** Verify the email address exists in the database
|
|
38
|
+
|
|
39
|
+
**Parameters:** `email`
|
|
40
|
+
|
|
41
|
+
**Translations:**
|
|
42
|
+
- DE: Benutzer mit E-Mail {email} wurde nicht gefunden.
|
|
43
|
+
- EN: User with email {email} not found.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
### LTNS_0002: invalidPassword
|
|
48
|
+
|
|
49
|
+
**Message:** Invalid password provided
|
|
50
|
+
|
|
51
|
+
**Description:** Thrown when password verification fails during authentication
|
|
52
|
+
|
|
53
|
+
**Solution:** Verify the password meets requirements and matches the stored hash
|
|
54
|
+
|
|
55
|
+
**Translations:**
|
|
56
|
+
- DE: Das eingegebene Passwort ist ungültig.
|
|
57
|
+
- EN: The provided password is invalid.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
### LTNS_0003: invalidToken
|
|
62
|
+
|
|
63
|
+
**Message:** Invalid or expired token
|
|
64
|
+
|
|
65
|
+
**Description:** Thrown when JWT validation fails
|
|
66
|
+
|
|
67
|
+
**Solution:** Request a new access token using refresh token or re-authenticate
|
|
68
|
+
|
|
69
|
+
**Translations:**
|
|
70
|
+
- DE: Der Token ist ungültig oder abgelaufen.
|
|
71
|
+
- EN: The token is invalid or has expired.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
### LTNS_0004: tokenExpired
|
|
76
|
+
|
|
77
|
+
**Message:** Token has expired
|
|
78
|
+
|
|
79
|
+
**Description:** Thrown when JWT expiration time has passed
|
|
80
|
+
|
|
81
|
+
**Solution:** Use refresh token to get new access token or re-authenticate
|
|
82
|
+
|
|
83
|
+
**Translations:**
|
|
84
|
+
- DE: Der Token ist abgelaufen. Bitte melden Sie sich erneut an.
|
|
85
|
+
- EN: The token has expired. Please sign in again.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
### LTNS_0005: refreshTokenRequired
|
|
90
|
+
|
|
91
|
+
**Message:** Refresh token is required
|
|
92
|
+
|
|
93
|
+
**Description:** Thrown when attempting to refresh without providing refresh token
|
|
94
|
+
|
|
95
|
+
**Solution:** Include the refresh token in the authorization header or cookie
|
|
96
|
+
|
|
97
|
+
**Translations:**
|
|
98
|
+
- DE: Ein Refresh-Token ist erforderlich.
|
|
99
|
+
- EN: A refresh token is required.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
### LTNS_0006: userNotVerified
|
|
104
|
+
|
|
105
|
+
**Message:** User email is not verified
|
|
106
|
+
|
|
107
|
+
**Description:** Thrown when user attempts action requiring verified status
|
|
108
|
+
|
|
109
|
+
**Solution:** Complete the email verification process
|
|
110
|
+
|
|
111
|
+
**Translations:**
|
|
112
|
+
- DE: Die E-Mail-Adresse wurde noch nicht verifiziert.
|
|
113
|
+
- EN: The email address has not been verified yet.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Authorization Errors (LTNS_0100-LTNS_0199)
|
|
118
|
+
|
|
119
|
+
### LTNS_0100: unauthorized
|
|
120
|
+
|
|
121
|
+
**Message:** Unauthorized access
|
|
122
|
+
|
|
123
|
+
**Description:** Thrown when accessing protected resource without authentication
|
|
124
|
+
|
|
125
|
+
**Solution:** Sign in to access this resource
|
|
126
|
+
|
|
127
|
+
**Translations:**
|
|
128
|
+
- DE: Sie sind nicht angemeldet.
|
|
129
|
+
- EN: You are not authenticated.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
### LTNS_0101: accessDenied
|
|
134
|
+
|
|
135
|
+
**Message:** Access denied - insufficient permissions
|
|
136
|
+
|
|
137
|
+
**Description:** Thrown when user does not have the required role
|
|
138
|
+
|
|
139
|
+
**Solution:** Contact an administrator to request the required permissions
|
|
140
|
+
|
|
141
|
+
**Parameters:** `requiredRole`
|
|
142
|
+
|
|
143
|
+
**Translations:**
|
|
144
|
+
- DE: Zugriff verweigert. Sie benötigen die Rolle {requiredRole}.
|
|
145
|
+
- EN: Access denied. Role {requiredRole} is required.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
### LTNS_0102: resourceForbidden
|
|
150
|
+
|
|
151
|
+
**Message:** Access to this resource is forbidden
|
|
152
|
+
|
|
153
|
+
**Description:** Thrown when user cannot access a resource they do not own
|
|
154
|
+
|
|
155
|
+
**Solution:** Verify you are the owner or have been granted access
|
|
156
|
+
|
|
157
|
+
**Parameters:** `resourceId`
|
|
158
|
+
|
|
159
|
+
**Translations:**
|
|
160
|
+
- DE: Der Zugriff auf diese Ressource ({resourceId}) ist nicht gestattet.
|
|
161
|
+
- EN: Access to this resource ({resourceId}) is forbidden.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## User Errors (LTNS_0200-LTNS_0299)
|
|
166
|
+
|
|
167
|
+
### LTNS_0200: emailAlreadyExists
|
|
168
|
+
|
|
169
|
+
**Message:** Email address already registered
|
|
170
|
+
|
|
171
|
+
**Description:** Thrown when attempting to register with an existing email
|
|
172
|
+
|
|
173
|
+
**Solution:** Use a different email address or recover the existing account
|
|
174
|
+
|
|
175
|
+
**Parameters:** `email`
|
|
176
|
+
|
|
177
|
+
**Translations:**
|
|
178
|
+
- DE: Die E-Mail-Adresse {email} ist bereits registriert.
|
|
179
|
+
- EN: The email address {email} is already registered.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
### LTNS_0201: usernameAlreadyExists
|
|
184
|
+
|
|
185
|
+
**Message:** Username already taken
|
|
186
|
+
|
|
187
|
+
**Description:** Thrown when attempting to register with an existing username
|
|
188
|
+
|
|
189
|
+
**Solution:** Choose a different username
|
|
190
|
+
|
|
191
|
+
**Parameters:** `username`
|
|
192
|
+
|
|
193
|
+
**Translations:**
|
|
194
|
+
- DE: Der Benutzername {username} ist bereits vergeben.
|
|
195
|
+
- EN: The username {username} is already taken.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Validation Errors (LTNS_0300-LTNS_0399)
|
|
200
|
+
|
|
201
|
+
### LTNS_0300: validationFailed
|
|
202
|
+
|
|
203
|
+
**Message:** Validation failed
|
|
204
|
+
|
|
205
|
+
**Description:** Thrown when input does not meet validation requirements
|
|
206
|
+
|
|
207
|
+
**Solution:** Check the validation rules and provide valid input
|
|
208
|
+
|
|
209
|
+
**Parameters:** `field`
|
|
210
|
+
|
|
211
|
+
**Translations:**
|
|
212
|
+
- DE: Validierung fehlgeschlagen für Feld {field}.
|
|
213
|
+
- EN: Validation failed for field {field}.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
### LTNS_0301: requiredFieldMissing
|
|
218
|
+
|
|
219
|
+
**Message:** Required field is missing
|
|
220
|
+
|
|
221
|
+
**Description:** Thrown when a required field is not included in the request
|
|
222
|
+
|
|
223
|
+
**Solution:** Include the required field in your request
|
|
224
|
+
|
|
225
|
+
**Parameters:** `field`
|
|
226
|
+
|
|
227
|
+
**Translations:**
|
|
228
|
+
- DE: Das Pflichtfeld {field} fehlt.
|
|
229
|
+
- EN: The required field {field} is missing.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
### LTNS_0302: invalidFieldFormat
|
|
234
|
+
|
|
235
|
+
**Message:** Invalid format for field
|
|
236
|
+
|
|
237
|
+
**Description:** Thrown when field value does not match expected format
|
|
238
|
+
|
|
239
|
+
**Solution:** Check the expected format and provide a valid value
|
|
240
|
+
|
|
241
|
+
**Parameters:** `field`, `expectedFormat`
|
|
242
|
+
|
|
243
|
+
**Translations:**
|
|
244
|
+
- DE: Ungültiges Format für {field}. Erwartet: {expectedFormat}.
|
|
245
|
+
- EN: Invalid format for {field}. Expected: {expectedFormat}.
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
### LTNS_0303: nonWhitelistedProperties
|
|
250
|
+
|
|
251
|
+
**Message:** Non-whitelisted properties found
|
|
252
|
+
|
|
253
|
+
**Description:** Thrown when request body contains properties not decorated with `@UnifiedField`. Only active when `nonWhitelistedFields: 'error'` is configured.
|
|
254
|
+
|
|
255
|
+
**Solution:** Remove the non-whitelisted properties from the request, or decorate them with `@UnifiedField` in the input class. If using `'strip'` mode (default), these properties are silently removed instead of throwing an error.
|
|
256
|
+
|
|
257
|
+
**Parameters:** `properties`
|
|
258
|
+
|
|
259
|
+
**Translations:**
|
|
260
|
+
- DE: Die folgenden Eigenschaften sind nicht erlaubt: {{properties}}. Nur mit @UnifiedField dekorierte Eigenschaften werden akzeptiert.
|
|
261
|
+
- EN: The following properties are not allowed: {{properties}}. Only properties decorated with @UnifiedField are accepted.
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Resource Errors (LTNS_0400-LTNS_0499)
|
|
266
|
+
|
|
267
|
+
### LTNS_0400: resourceNotFound
|
|
268
|
+
|
|
269
|
+
**Message:** Resource not found
|
|
270
|
+
|
|
271
|
+
**Description:** Thrown when the requested resource does not exist
|
|
272
|
+
|
|
273
|
+
**Solution:** Verify the resource ID is correct
|
|
274
|
+
|
|
275
|
+
**Parameters:** `resourceType`, `resourceId`
|
|
276
|
+
|
|
277
|
+
**Translations:**
|
|
278
|
+
- DE: {resourceType} mit ID {resourceId} wurde nicht gefunden.
|
|
279
|
+
- EN: {resourceType} with ID {resourceId} was not found.
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
### LTNS_0401: resourceAlreadyExists
|
|
284
|
+
|
|
285
|
+
**Message:** Resource already exists
|
|
286
|
+
|
|
287
|
+
**Description:** Thrown when attempting to create a resource that already exists
|
|
288
|
+
|
|
289
|
+
**Solution:** Use update operation or choose a different identifier
|
|
290
|
+
|
|
291
|
+
**Parameters:** `resourceType`, `identifier`
|
|
292
|
+
|
|
293
|
+
**Translations:**
|
|
294
|
+
- DE: {resourceType} mit Kennung {identifier} existiert bereits.
|
|
295
|
+
- EN: {resourceType} with identifier {identifier} already exists.
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## File Errors (LTNS_0500-LTNS_0599)
|
|
300
|
+
|
|
301
|
+
### LTNS_0500: fileNotFound
|
|
302
|
+
|
|
303
|
+
**Message:** File not found
|
|
304
|
+
|
|
305
|
+
**Description:** Thrown when the requested file does not exist
|
|
306
|
+
|
|
307
|
+
**Solution:** Verify the file ID is correct
|
|
308
|
+
|
|
309
|
+
**Parameters:** `fileId`
|
|
310
|
+
|
|
311
|
+
**Translations:**
|
|
312
|
+
- DE: Datei mit ID {fileId} wurde nicht gefunden.
|
|
313
|
+
- EN: File with ID {fileId} was not found.
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
### LTNS_0501: fileUploadFailed
|
|
318
|
+
|
|
319
|
+
**Message:** File upload failed
|
|
320
|
+
|
|
321
|
+
**Description:** Thrown when file upload process encounters an error
|
|
322
|
+
|
|
323
|
+
**Solution:** Check file size limits, allowed formats, and try again
|
|
324
|
+
|
|
325
|
+
**Parameters:** `reason`
|
|
326
|
+
|
|
327
|
+
**Translations:**
|
|
328
|
+
- DE: Datei-Upload fehlgeschlagen: {reason}.
|
|
329
|
+
- EN: File upload failed: {reason}.
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
### LTNS_0502: fileTypeNotAllowed
|
|
334
|
+
|
|
335
|
+
**Message:** File type not allowed
|
|
336
|
+
|
|
337
|
+
**Description:** Thrown when uploaded file type is not in the allowed list
|
|
338
|
+
|
|
339
|
+
**Solution:** Convert or upload a file with an allowed type
|
|
340
|
+
|
|
341
|
+
**Parameters:** `fileType`, `allowedTypes`
|
|
342
|
+
|
|
343
|
+
**Translations:**
|
|
344
|
+
- DE: Dateityp {fileType} ist nicht erlaubt. Erlaubt: {allowedTypes}.
|
|
345
|
+
- EN: File type {fileType} is not allowed. Allowed: {allowedTypes}.
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
## Internal Errors (LTNS_0900-LTNS_0999)
|
|
350
|
+
|
|
351
|
+
### LTNS_0900: internalError
|
|
352
|
+
|
|
353
|
+
**Message:** An internal error occurred
|
|
354
|
+
|
|
355
|
+
**Description:** Thrown when an unexpected internal error occurs
|
|
356
|
+
|
|
357
|
+
**Solution:** Contact support if the issue persists
|
|
358
|
+
|
|
359
|
+
**Translations:**
|
|
360
|
+
- DE: Ein interner Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.
|
|
361
|
+
- EN: An internal error occurred. Please try again later.
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
### LTNS_0901: serviceUnavailable
|
|
366
|
+
|
|
367
|
+
**Message:** Service temporarily unavailable
|
|
368
|
+
|
|
369
|
+
**Description:** Thrown when an external service is not responding
|
|
370
|
+
|
|
371
|
+
**Solution:** Try again later or contact support
|
|
372
|
+
|
|
373
|
+
**Parameters:** `serviceName`
|
|
374
|
+
|
|
375
|
+
**Translations:**
|
|
376
|
+
- DE: Der Dienst {serviceName} ist vorübergehend nicht verfügbar.
|
|
377
|
+
- EN: Service {serviceName} is temporarily unavailable.
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
### LTNS_0902: legacyAuthDisabled
|
|
382
|
+
|
|
383
|
+
**Message:** Legacy authentication is disabled
|
|
384
|
+
|
|
385
|
+
**Description:** Thrown when trying to use disabled legacy auth endpoints
|
|
386
|
+
|
|
387
|
+
**Solution:** Migrate to BetterAuth (IAM) endpoints for authentication
|
|
388
|
+
|
|
389
|
+
**Parameters:** `endpoint`
|
|
390
|
+
|
|
391
|
+
**Translations:**
|
|
392
|
+
- DE: Der Legacy-Authentifizierungs-Endpoint {endpoint} ist deaktiviert. Bitte verwenden Sie BetterAuth (IAM).
|
|
393
|
+
- EN: Legacy authentication endpoint {endpoint} is disabled. Please use BetterAuth (IAM).
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
## Extending Error Codes
|
|
398
|
+
|
|
399
|
+
Projects can extend the error code system by creating their own error registry:
|
|
400
|
+
|
|
401
|
+
```typescript
|
|
402
|
+
import { defineErrors, createProjectErrors, CoreErrorCodeService } from '@lenne.tech/nest-server';
|
|
403
|
+
|
|
404
|
+
// Define project-specific errors
|
|
405
|
+
const PROJECT_ERRORS = defineErrors({
|
|
406
|
+
PROJ_0001: {
|
|
407
|
+
name: 'orderNotFound',
|
|
408
|
+
message: 'Order not found',
|
|
409
|
+
params: ['orderId'] as const,
|
|
410
|
+
translations: {
|
|
411
|
+
de: 'Bestellung {orderId} nicht gefunden.',
|
|
412
|
+
en: 'Order {orderId} not found.',
|
|
413
|
+
},
|
|
414
|
+
docs: {
|
|
415
|
+
description: 'Thrown when order lookup fails',
|
|
416
|
+
solution: 'Verify the order ID',
|
|
417
|
+
},
|
|
418
|
+
},
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
// Create factory functions
|
|
422
|
+
export const ProjectErrors = createProjectErrors(PROJECT_ERRORS);
|
|
423
|
+
|
|
424
|
+
// Extend the service to register project errors
|
|
425
|
+
@Injectable()
|
|
426
|
+
export class ErrorCodeService extends CoreErrorCodeService {
|
|
427
|
+
constructor() {
|
|
428
|
+
super();
|
|
429
|
+
this.registerErrors(PROJECT_ERRORS);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
## Usage in Code
|
|
435
|
+
|
|
436
|
+
```typescript
|
|
437
|
+
import { Errors } from '@lenne.tech/nest-server';
|
|
438
|
+
|
|
439
|
+
// Throw an error with parameters
|
|
440
|
+
throw Errors.userNotFound('user@example.com');
|
|
441
|
+
// → "#LTNS_0001: User not found with given email"
|
|
442
|
+
|
|
443
|
+
// Throw an error without parameters
|
|
444
|
+
throw Errors.invalidPassword();
|
|
445
|
+
// → "#LTNS_0002: Invalid password provided"
|
|
446
|
+
```
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
# Migration Guide: 11.10.x → 11.11.x
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
| Category | Details |
|
|
6
|
+
|----------|---------|
|
|
7
|
+
| **Breaking Changes** | None |
|
|
8
|
+
| **New Features** | **PaginationInfo model** for paginated queries with navigation metadata |
|
|
9
|
+
| **Bugfixes** | None |
|
|
10
|
+
| **Migration Effort** | Very Low (~5 minutes) - No changes required, opt-in feature |
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Quick Migration
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Update package
|
|
18
|
+
npm install @lenne.tech/nest-server@11.11.x
|
|
19
|
+
|
|
20
|
+
# Verify build
|
|
21
|
+
npm run build
|
|
22
|
+
|
|
23
|
+
# Run tests
|
|
24
|
+
npm test
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**That's it!** This is a non-breaking update. The new `PaginationInfo` feature is opt-in.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## What's New in 11.11.x
|
|
32
|
+
|
|
33
|
+
### 1. PaginationInfo Model for API Navigation
|
|
34
|
+
|
|
35
|
+
A new `PaginationInfo` model provides pagination metadata for paginated queries, making it easier to implement frontend pagination UI components.
|
|
36
|
+
|
|
37
|
+
**New Fields Available:**
|
|
38
|
+
|
|
39
|
+
| Field | Type | Description |
|
|
40
|
+
|-------|------|-------------|
|
|
41
|
+
| `totalCount` | number | Total number of items across all pages |
|
|
42
|
+
| `pageCount` | number | Total number of pages |
|
|
43
|
+
| `currentPage` | number | Current page number (1-based) |
|
|
44
|
+
| `perPage` | number | Number of items per page |
|
|
45
|
+
| `hasNextPage` | boolean | Indicates if there is a next page |
|
|
46
|
+
| `hasPreviousPage` | boolean | Indicates if there is a previous page |
|
|
47
|
+
|
|
48
|
+
**GraphQL Response Structure:**
|
|
49
|
+
|
|
50
|
+
The `findAndCount` method now returns an additional `pagination` field alongside the existing `items` and `totalCount`:
|
|
51
|
+
|
|
52
|
+
```graphql
|
|
53
|
+
query {
|
|
54
|
+
findAndCountUsers(skip: 0, take: 10) {
|
|
55
|
+
items {
|
|
56
|
+
id
|
|
57
|
+
email
|
|
58
|
+
}
|
|
59
|
+
totalCount
|
|
60
|
+
pagination {
|
|
61
|
+
totalCount
|
|
62
|
+
pageCount
|
|
63
|
+
currentPage
|
|
64
|
+
perPage
|
|
65
|
+
hasNextPage
|
|
66
|
+
hasPreviousPage
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Example Response:**
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"data": {
|
|
77
|
+
"findAndCountUsers": {
|
|
78
|
+
"items": [...],
|
|
79
|
+
"totalCount": 47,
|
|
80
|
+
"pagination": {
|
|
81
|
+
"totalCount": 47,
|
|
82
|
+
"pageCount": 5,
|
|
83
|
+
"currentPage": 1,
|
|
84
|
+
"perPage": 10,
|
|
85
|
+
"hasNextPage": true,
|
|
86
|
+
"hasPreviousPage": false
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 2. Using PaginationInfo in Custom Outputs
|
|
94
|
+
|
|
95
|
+
To use `PaginationInfo` in your own `findAndCount` result types, add the pagination field:
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
import { ObjectType } from '@nestjs/graphql';
|
|
99
|
+
import { PaginationInfo, UnifiedField } from '@lenne.tech/nest-server';
|
|
100
|
+
import { YourModel } from './your.model';
|
|
101
|
+
|
|
102
|
+
@ObjectType({ description: 'Result of find and count' })
|
|
103
|
+
export class FindAndCountYourModelResult {
|
|
104
|
+
@UnifiedField({
|
|
105
|
+
description: 'Found items',
|
|
106
|
+
isArray: true,
|
|
107
|
+
isOptional: true,
|
|
108
|
+
type: () => YourModel,
|
|
109
|
+
})
|
|
110
|
+
items: YourModel[];
|
|
111
|
+
|
|
112
|
+
@UnifiedField({
|
|
113
|
+
description: 'Total count (skip/offset and limit/take are ignored)',
|
|
114
|
+
isOptional: false,
|
|
115
|
+
})
|
|
116
|
+
totalCount: number;
|
|
117
|
+
|
|
118
|
+
// NEW: Add pagination field
|
|
119
|
+
@UnifiedField({
|
|
120
|
+
description: 'Pagination information',
|
|
121
|
+
isOptional: true,
|
|
122
|
+
type: () => PaginationInfo,
|
|
123
|
+
})
|
|
124
|
+
pagination?: PaginationInfo;
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 3. PaginationInfo.create() Static Method
|
|
129
|
+
|
|
130
|
+
The `PaginationInfo` model provides a static `create()` method for generating pagination metadata:
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
import { PaginationInfo } from '@lenne.tech/nest-server';
|
|
134
|
+
|
|
135
|
+
// Create pagination info from query parameters
|
|
136
|
+
const pagination = PaginationInfo.create({
|
|
137
|
+
skip: 20, // or offset
|
|
138
|
+
take: 10, // or limit
|
|
139
|
+
totalCount: 47,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Result:
|
|
143
|
+
// {
|
|
144
|
+
// totalCount: 47,
|
|
145
|
+
// pageCount: 5,
|
|
146
|
+
// currentPage: 3,
|
|
147
|
+
// perPage: 10,
|
|
148
|
+
// hasNextPage: true,
|
|
149
|
+
// hasPreviousPage: true,
|
|
150
|
+
// }
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**Edge Cases:**
|
|
154
|
+
|
|
155
|
+
| Scenario | Result |
|
|
156
|
+
|----------|--------|
|
|
157
|
+
| Empty results (`totalCount: 0`) | `currentPage: 0`, `pageCount: 0`, `hasNextPage: false`, `hasPreviousPage: false` |
|
|
158
|
+
| No limit specified (`take: 0`) | All items treated as single page, `perPage: 0` |
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Breaking Changes
|
|
163
|
+
|
|
164
|
+
**None.** This is a fully backward-compatible update.
|
|
165
|
+
|
|
166
|
+
- Existing `findAndCount` queries continue to work without changes
|
|
167
|
+
- The `pagination` field is optional and only included when requested in the GraphQL query
|
|
168
|
+
- Existing `items` and `totalCount` fields remain unchanged
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Compatibility Notes
|
|
173
|
+
|
|
174
|
+
### Existing findAndCount Queries
|
|
175
|
+
|
|
176
|
+
Queries that don't request the `pagination` field continue to work unchanged:
|
|
177
|
+
|
|
178
|
+
```graphql
|
|
179
|
+
# Still works exactly as before
|
|
180
|
+
query {
|
|
181
|
+
findAndCountUsers(skip: 0, take: 10) {
|
|
182
|
+
items { id email }
|
|
183
|
+
totalCount
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### CrudService.findAndCount Return Type
|
|
189
|
+
|
|
190
|
+
The return type of `findAndCount` now includes `pagination`:
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
// Return type
|
|
194
|
+
Promise<{
|
|
195
|
+
items: Model[];
|
|
196
|
+
pagination: PaginationInfo;
|
|
197
|
+
totalCount: number;
|
|
198
|
+
}>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
This is backward compatible because:
|
|
202
|
+
- The `pagination` property is automatically calculated
|
|
203
|
+
- TypeScript will accept the extended return type
|
|
204
|
+
- GraphQL clients only receive fields they request
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Troubleshooting
|
|
209
|
+
|
|
210
|
+
### Pagination Field Not Available in GraphQL
|
|
211
|
+
|
|
212
|
+
**Symptom:** The `pagination` field is not available when querying `findAndCount`.
|
|
213
|
+
|
|
214
|
+
**Solution:** Ensure your output type includes the `pagination` field:
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
@UnifiedField({
|
|
218
|
+
description: 'Pagination information',
|
|
219
|
+
isOptional: true,
|
|
220
|
+
type: () => PaginationInfo,
|
|
221
|
+
})
|
|
222
|
+
pagination?: PaginationInfo;
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### currentPage Shows 0
|
|
226
|
+
|
|
227
|
+
**Symptom:** `currentPage` returns 0 even with items in the result.
|
|
228
|
+
|
|
229
|
+
**Cause:** This happens when `totalCount` is 0 (empty result set).
|
|
230
|
+
|
|
231
|
+
**Expected Behavior:** When there are no results, `currentPage` is 0 to indicate "no page".
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Module Documentation
|
|
236
|
+
|
|
237
|
+
### CrudService
|
|
238
|
+
|
|
239
|
+
- **Location:** `src/core/common/services/crud.service.ts`
|
|
240
|
+
- **Key Method:** `findAndCount()` - Now returns `pagination` alongside `items` and `totalCount`
|
|
241
|
+
|
|
242
|
+
### PaginationInfo Model
|
|
243
|
+
|
|
244
|
+
- **Location:** `src/core/common/models/pagination-info.model.ts`
|
|
245
|
+
- **Export:** `@lenne.tech/nest-server`
|
|
246
|
+
- **Reference Implementation:** `src/server/modules/user/outputs/find-and-count-users-result.output.ts`
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## New Exports
|
|
251
|
+
|
|
252
|
+
The following is now exported from `@lenne.tech/nest-server`:
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
// PaginationInfo model (11.11.0)
|
|
256
|
+
export { PaginationInfo } from './core/common/models/pagination-info.model';
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## References
|
|
262
|
+
|
|
263
|
+
- [CrudService](../src/core/common/services/crud.service.ts) - Base service with `findAndCount` method
|
|
264
|
+
- [PaginationInfo Model](../src/core/common/models/pagination-info.model.ts) - Pagination metadata model
|
|
265
|
+
- [User Output Example](../src/server/modules/user/outputs/find-and-count-users-result.output.ts) - Reference implementation
|
|
266
|
+
- [nest-server-starter](https://github.com/lenneTech/nest-server-starter) (reference implementation)
|