perfect-payload 1.1.2 → 1.2.0-beta.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/README.md CHANGED
@@ -1,375 +1,1863 @@
1
- # Data Validation Module
2
-
3
- This module provides a robust framework for validating data objects based on defined rules. It includes various validation attributes, types, custom error messages, and default values to ensure data integrity and consistency.
4
-
5
- ## Table of Contents
6
-
7
- 1. [Quick Sight](#default-values)
8
- 2. [Available Validation Attributes](#available-validation-attributes)
9
- 3. [Available Types](#available-types)
10
- 4. [Custom Error Message Attributes](#custom-error-message-attributes)
11
- 5. [Default Values](#default-values)
12
- 6. [Examples and Usage](#default-values)
13
-
14
- ## 1. Quick Sights
15
-
16
- usage:
17
-
18
- ```javascript
19
- import { perfectPayloadV1 } from "perfect-payload";
20
- const { statucCode = 400, ...result } = perfectPayloadV1(
21
- data,
22
- dataValidationRule,
23
- validPayloadResponse,
24
- inValidPayloadResponse
25
- );
26
- ```
27
-
28
- input:
29
-
30
- 1. **data :** your payload object (required \*)
31
- 2. **dataValidationRule:** validation rule object (required \*)
32
- 3. **validPayloadResponse:** response object you want it back on all validation passed (Optional)
33
- 4. **inValidPayloadResponse:** response object you want it back on any validation fails (Optional)
34
-
35
- **Default Valid Payload Response:**
36
-
37
- ```javascript
38
- {
39
- statusCode:200,
40
- valid:true,
41
- validatedPayload:{...}
42
- }
43
- ```
44
-
45
- **Note:** you can use validatedPayload to overwrite your existing `req.body` or create new req attribute(`req.validatedBody=validatedPayload`) to access in your API logic
46
-
47
-
48
- **Default Invalid Payload Response:**
49
-
50
- ```javascript
51
- {
52
- statusCode:400,
53
- valid:false,
54
- message:"One or more attribute values are invalid",
55
- errors:["minSalary must be less than maxSalary"]
56
- }
57
- ```
58
-
59
- **Note:** If inValidPayloadResponse is passed, then it will be returned along with errors property(avoid error attribute in your inValidPayloadResponse object)
60
-
61
- ## 2. Available Validation Attributes
62
-
63
- These attributes define the rules that can be applied to each field in the data object:
64
-
65
- - **mandatory**: Specifies whether the data is required.
66
- - **allowNull**: Allows `null` values.
67
- - **allowEmptyObject**: Allows empty objects `{}`.
68
- - **allowEmptyArray**: Allows empty arrays `[]`.
69
- - **elementConstraints**: Defines the constraints of elements in an array (refer to [Available ](#available-types)[Attributes](#available-validation-attributes) section).
70
- - **regex**: Applies custom regular expression validation.
71
- - **type**: Specifies the type of the field (refer to [Available Types](#available-types) section).
72
- - **minLength**: Minimum length for string values.
73
- - **maxLength**: Maximum length for string values.
74
- - **preventDecimal**: Disallows decimal or fractional values.
75
- - **min**: Specifies the minimum number value.
76
- - **max**: Specifies the maximum number value.
77
- - **range**: Specifies a numeric range for the value.
78
- - **objectAttr**: Validates nested objects.
79
- - **dependency**: Validates fields that depend on the values of other fields (e.g., min and max salary).
80
-
81
- ## 3. Available Types
82
-
83
- These types can be used in the `type` attribute to specify the expected data type:
84
-
85
- - **number**: Numeric values.
86
- - **string**: String values.
87
- - **boolean**: Boolean values (`true`/`false`).
88
- - **email**: Valid email addresses.
89
- - **url**: Valid URL formats.
90
- - **enum**: A specific set of allowed values (supports heterogeneous arrays).
91
- - **uuid**: Valid UUIDs (supports all versions).
92
- - **uuidv1/uuidv3/uuidv4/uuidv5**: Version-specific UUID validation.
93
- - **objectId**: Valid MongoDB ObjectIds.
94
- - **array**: Validates arrays, with support for element type validation using `elementConstraints`.
95
- - **object**: Validates objects, including nested objects using `objectAttr`.
96
-
97
- ## 4. Custom Error Message Attributes
98
-
99
- You can define custom error messages for various validation failures using these attributes:
100
-
101
- - **mandatoryError**: Custom message when a mandatory field is missing.
102
- - **allowNullError**: Custom message when a `null` value is not allowed.
103
- - **emptyObjectError**: Custom message when an empty object `{}` is not allowed.
104
- - **emptyArrayError**: Custom message when an empty array `[]` is not allowed.
105
- - **elementConstraintsError**: Custom message when array elements do not match the passed constraints.
106
- - **regexError**: Custom message when a value does not match the specified regex pattern.
107
- - **typeError**: Custom message when a value does not match the specified type.
108
- - **minLengthError**: Custom message when a string value is shorter than the minimum length.
109
- - **maxLengthError**: Custom message when a string value exceeds the maximum length.
110
- - **preventDecimalError**: Custom message when a decimal value is not allowed.
111
- - **minError**: Custom message when a value is less than the minimum allowed.
112
- - **maxError**: Custom message when a value exceeds the maximum allowed.
113
- - **rangeError**: Custom message when a value is outside the specified range.
114
-
115
- ## 5. Default Values
116
-
117
- If not explicitly specified, the following default values are applied:
118
-
119
- - **mandatory**: `false` (field is not required).
120
- - **allowNull**: `true` (allows `null` values).
121
- - **allowEmptyObject**: `true` (allows empty objects `{}`).
122
- - **allowEmptyArray**: `true` (allows empty arrays `[]`).
123
- - **elementConstraints**: Ignored.
124
- - **regex**: Ignored.
125
- - **type**: Ignored.
126
- - **minLength**: Ignored.
127
- - **maxLength**: Ignored.
128
- - **preventDecimal**: `false` (allows both decimal and integer values).
129
- - **min**: Ignored.
130
- - **max**: Ignored.
131
- - **range**: Ignored.
132
- - **objectAttr**: Ignored.
133
- - **dependency**: Ignored.
134
-
135
- ## 6. Examples And Usage
136
-
137
- ### 1. Sample Validation Rule
138
-
139
- sample-1
140
-
141
- ```javascript
142
- {
143
- firstName: {
144
- mandatory: true,
145
- allowNull: false,
146
- type: "string",
147
- minLength: 3,
148
- minLengthError:"First name must have minimum 3 characters."
149
- },
150
- lastName: {
151
- mandatory: false,
152
- allowNull: true,
153
- type: "string",
154
- },
155
- email: {
156
- mandatory: true,
157
- allowNull: false,
158
- type: "email",
159
- },
160
- phone: {
161
- mandatory: true,
162
- allowNull: false,
163
- type: "string",
164
- },
165
- age: {
166
- mandatory: false,
167
- type: "number",
168
- min: 1,
169
- max: 120,
170
- },
171
- };
172
- ```
173
-
174
- sample-2
175
-
176
- ```javascript
177
- {
178
- id: {
179
- mandatory: true,
180
- allowNull: true,
181
- type: "uuidv4",
182
- },
183
- batchId: {
184
- mandatory: true,
185
- allowNull: true,
186
- type: "objectId",
187
- },
188
- firstName: {
189
- mandatory: true,
190
- type: "string",
191
- minLength: 3,
192
- },
193
- lastName: {
194
- mandatory: false,
195
- allowNull: true,
196
- type: "string",
197
- },
198
- age: {
199
- type: "number",
200
- min: 0.1,
201
- max: 120,
202
- },
203
- isAdult: {
204
- type: "boolean",
205
- },
206
- totalWins: {
207
- type: "number",
208
- min: 0,
209
- preventDecimal: true,
210
- },
211
- email: {
212
- regex: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
213
- },
214
- githubLink: {
215
- type: "url",
216
- },
217
- accountStatus: {
218
- type: "enum",
219
- enumValues: ["Active", "Inactive", 200],
220
- },
221
- marks: {
222
- range: "0-100",
223
- },
224
- allMarks: {
225
- type: "array",
226
- allowEmptyArray: false,
227
- elementConstraints: {
228
- type: "number",
229
- allowNull: false,
230
- range: "0-100",
231
- },
232
- },
233
- totalScore: {
234
- type: "number",
235
- dependency: {
236
- result: {
237
- setDependencyRule: (totalScore, result) => {
238
- return { mandatory: true, allowNull: false, type: "string" };
239
- },
240
- },
241
- },
242
- },
243
- result: {
244
- type: "string",
245
- dependency: {
246
- totalScore: {
247
- setDependencyRule: (result, totalScore) => {
248
- return { mandatory: true, allowNull: false, type: "number" };
249
- },
250
- },
251
- },
252
- },
253
- minSalary: {
254
- mandatory: true,
255
- min: 1,
256
- type: "number",
257
- dependency: {
258
- maxSalary: {
259
- setDependencyRule: (minSalary, maxSalary) => {
260
- return {
261
- mandatory: true,
262
- min: minSalary + 1,
263
- minError: "maxSalary must be more than minSalary",
264
- };
265
- },
266
- },
267
- },
268
- },
269
- maxSalary: {
270
- dependency: {
271
- minSalary: {
272
- setDependencyRule: (maxSalary, minSalary) => {
273
- return {
274
- mandatory: true,
275
- max: maxSalary - 1,
276
- maxError: "minSalary must be less than maxSalary",
277
- };
278
- },
279
- },
280
- },
281
- },
282
- address: {
283
- mandatory: true,
284
- type: "object",
285
- allowEmptyObject: false,
286
- objectAttr: {
287
- country: { mandatory: true, type: "string" },
288
- state: {
289
- mandatory: true,
290
- type: "string",
291
- },
292
- city: {},
293
- zip: {
294
- mandatory: true,
295
- type: "string",
296
- },
297
- position: {
298
- mandatory: true,
299
- type: "object",
300
- allowEmptyObject: false,
301
- objectAttr: {
302
- lattitude: { mandatory: true, type: "number" },
303
- longitude: {
304
- mandatory: true,
305
- type: "number",
306
- },
307
- },
308
- },
309
- },
310
- },
311
- }
312
- ```
313
-
314
- ### 2. Usage
315
-
316
- #### 1. creating your route with payload validation middleware
317
-
318
- ```javascript
319
- //Here validatePayload is your middleware function, where you're invoking perfect payload
320
- router.post(
321
- "/payload-validation",
322
- validatePayload({ rule: <your validation rule json object> }),
323
- (req, res) => res.send("OK")
324
- );
325
- ```
326
-
327
- #### 2.1 Use perfect-payload in your middleware like below(for MODULE JS)
328
-
329
- ```javascript
330
- import { perfectPayloadV1 } from "perfect-payload";
331
-
332
- export const validatePayload = ({ rule }) => {
333
- return (req, res, next) => {
334
- try {
335
- const { statusCode, ...response } = perfectPayloadV1(req?.body, rule);
336
- if (+statusCode >= 200 && +statusCode <= 299) {
337
- req.validatedBody=response?.validatedPayload
338
- next();
339
- } else res.status(statusCode).json(response);
340
- } catch (error) {
341
- console.error("Error validating payload", error);
342
- res.status(500).json({ error: "Internal Server Error" });
343
- }
344
- };
345
- };
346
- ```
347
-
348
- #### 2.2 Use perfect-payload in your middleware like below(for COMMON JS)
349
-
350
- ```javascript
351
- function validatePayload({ rule }) {
352
- return async (req, res, next) => {
353
- try {
354
- const { perfectPayloadV1 } = await import("perfect-payload");
355
- const { statusCode, ...response } = perfectPayloadV1(req?.body, rule);
356
-
357
- if (+statusCode >= 200 && +statusCode <= 299) {
358
- req.validatedBody=response?.validatedPayload
359
- next();
360
- } else {
361
- res.status(statusCode).json(response);
362
- }
363
- } catch (error) {
364
- console.error("Error validating payload", error);
365
- res.status(500).json({ error: "Internal Server Error" });
366
- }
367
- };
368
- }
369
-
370
- module.exports = { validatePayload };
371
- ```
372
-
373
- ---
374
-
375
- This documentation provides a comprehensive guide to using the data validation module effectively. Ensure to define your validation rules clearly to maintain data quality and consistency in your applications.
1
+ # perfect-payload
2
+
3
+ A lightweight JavaScript payload validation utility for validating API
4
+ and JSON payloads with simple rule-based configuration.
5
+
6
+ ## Installation
7
+
8
+ ```bash
9
+
10
+ npm install perfect-payload
11
+ ```
12
+
13
+ ## Basic Usage
14
+
15
+ Use `perfectPayload()` for all new implementations.
16
+
17
+ ```js
18
+ import { perfectPayload } from "perfect-payload";
19
+
20
+ const payload = {
21
+ name: "Kiran",
22
+
23
+ email: "kiran@example.com",
24
+
25
+ age: 29,
26
+ };
27
+
28
+ const validationRules = {
29
+ name: {
30
+ mandatory: true,
31
+
32
+ type: "string",
33
+ },
34
+
35
+ email: {
36
+ mandatory: true,
37
+
38
+ type: "email",
39
+ },
40
+
41
+ age: {
42
+ mandatory: true,
43
+
44
+ type: "number",
45
+
46
+ min: 18,
47
+ },
48
+ };
49
+
50
+ const result = perfectPayload(payload, validationRules);
51
+
52
+ console.log(result);
53
+ ```
54
+
55
+ ### Valid Response
56
+
57
+ ```js
58
+
59
+ {
60
+
61
+   statusCode: 200,
62
+
63
+   valid: true,
64
+
65
+   validatedPayload: {
66
+
67
+     name: "Kiran",
68
+
69
+     email: "kiran@example.com",
70
+
71
+     age: 29
72
+
73
+   }
74
+
75
+ }
76
+ ```
77
+
78
+ **Note:** The validatedPayload contains only the fields defined in the
79
+ schema, automatically filtering out any extra attributes. You can use it
80
+ to safely overwrite request.body or assign it to a new request property
81
+ (such as validatedBody, sanitisedData or parsedBody).
82
+
83
+ ### Invalid Response
84
+
85
+ ```js
86
+
87
+ {
88
+
89
+   statusCode: 400,
90
+
91
+   valid: false,
92
+
93
+   message: "One or more attribute values are invalid",
94
+
95
+   errors: [
96
+
97
+     {
98
+
99
+       path: "email",
100
+
101
+       code: "INVALID_EMAIL",
102
+
103
+       message: "Invalid email format for attribute email"
104
+
105
+     }
106
+
107
+   ]
108
+
109
+ }
110
+ ```
111
+
112
+ Each error returned by `perfectPayload()` contains:
113
+
114
+ ```js
115
+
116
+ {
117
+
118
+   path: "field.path",
119
+
120
+   code: "ERROR_CODE",
121
+
122
+   message: "Human readable validation message"
123
+
124
+ }
125
+ ```
126
+
127
+ - `path` identifies the exact field that failed validation.
128
+ - `code` provides a stable machine-readable validation error code.
129
+ - `message` provides a human-readable description of the validation
130
+ failure.
131
+ - Submitted payload values are not included in default error messages.
132
+
133
+ ## Legacy API
134
+
135
+ `perfectPayloadV1()` is still available for backward compatibility.
136
+
137
+ ```js
138
+ import { perfectPayloadV1 } from "perfect-payload";
139
+ ```
140
+
141
+ `perfectPayloadV1()` is deprecated and will no longer be supported after
142
+ **\*\*March 31, 2027\*\***.
143
+
144
+ Existing applications can continue using it during the migration period,
145
+ but all new implementations should use:
146
+
147
+ ```js
148
+ perfectPayload();
149
+ ```
150
+
151
+ The legacy API continues to return validation errors as:
152
+
153
+ ```js
154
+ errors: ["email is invalid"];
155
+ ```
156
+
157
+ while the new `perfectPayload()` API returns structured errors:
158
+
159
+ ```js
160
+ errors: [
161
+ {
162
+ path: "email",
163
+
164
+ code: "INVALID_EMAIL",
165
+
166
+ message: "Invalid email format for attribute email",
167
+ },
168
+ ];
169
+ ```
170
+
171
+ **Note:** If an inValidPayloadResponse is provided, the system returns
172
+ it alongside an automatically generated errors property. Do not include
173
+ your own errors attribute inside the custom inValidPayloadResponse
174
+ object.
175
+
176
+ ## Validation Rules
177
+
178
+ `perfectPayload()` supports the following validation rules.
179
+
180
+ ### `mandatory`
181
+
182
+ Marks a field as required(even empty string also not allowed)
183
+
184
+ **Default:** `false`, the field is not required.
185
+
186
+ ```js
187
+ const rules = {
188
+ name: {
189
+ mandatory: true,
190
+ },
191
+ };
192
+ ```
193
+
194
+ Error code: `REQUIRED`
195
+
196
+ ---
197
+
198
+ ### `allowNull`
199
+
200
+ Controls whether `null` values are accepted.
201
+
202
+ **Default:** `true` , `null` values are allowed.
203
+
204
+ Example:
205
+
206
+ ```js
207
+ const rules = {
208
+ name: {
209
+ allowNull: false,
210
+ },
211
+ };
212
+ ```
213
+
214
+ Error code: `NULL_NOT_ALLOWED`
215
+
216
+ ---
217
+
218
+ ### `allowEmptyObject`
219
+
220
+ Controls whether an empty object `{}` is accepted.
221
+
222
+ **Default:** `true`, empty objects are allowed.
223
+
224
+ Example:
225
+
226
+ ```js
227
+ const rules = {
228
+ address: {
229
+ type: "object",
230
+
231
+ allowEmptyObject: false,
232
+ },
233
+ };
234
+ ```
235
+
236
+ Error code: `EMPTY_OBJECT_NOT_ALLOWED`
237
+
238
+ ---
239
+
240
+ ### `allowEmptyArray`
241
+
242
+ Controls whether an empty array `[]` is accepted.
243
+
244
+ **Default:** `true`, empty arrays are allowed.
245
+
246
+ Example:
247
+
248
+ ```js
249
+ const rules = {
250
+ products: {
251
+ type: "array",
252
+
253
+ allowEmptyArray: false,
254
+ },
255
+ };
256
+ ```
257
+
258
+ Error code: `EMPTY_ARRAY_NOT_ALLOWED`
259
+
260
+ ---
261
+
262
+ ### `type`
263
+
264
+ Validates the expected data type.
265
+
266
+ Supported values:
267
+
268
+ ```text
269
+
270
+ number
271
+
272
+ string
273
+
274
+ boolean
275
+
276
+ email
277
+
278
+ url
279
+
280
+ enum
281
+
282
+ uuid
283
+
284
+ uuidv1
285
+
286
+ uuidv3
287
+
288
+ uuidv4
289
+
290
+ uuidv5
291
+
292
+ objectId
293
+
294
+ array
295
+
296
+ object
297
+ ```
298
+
299
+ Example:
300
+
301
+ ```js
302
+ const rules = {
303
+ age: {
304
+ type: "number",
305
+ },
306
+
307
+ email: {
308
+ type: "email",
309
+ },
310
+
311
+ active: {
312
+ type: "boolean",
313
+ },
314
+ };
315
+ ```
316
+
317
+ ### `enum`
318
+
319
+ A specific set of allowed values (supports heterogeneous arrays)
320
+
321
+ ```js
322
+ type: "enum";
323
+ ```
324
+
325
+ Example:
326
+
327
+ ```js
328
+ const rules = {
329
+ status: {
330
+ type: "enum",
331
+
332
+ enumValues: ["active", "inactive", "blocked", 1, 0],
333
+ },
334
+ };
335
+ ```
336
+
337
+ ### `enumValues`
338
+
339
+ Used together with:
340
+
341
+ ```js
342
+ type: "enum";
343
+ ```
344
+
345
+ Example:
346
+
347
+ ```js
348
+ const rules = {
349
+ status: {
350
+ type: "enum",
351
+
352
+ enumValues: ["active", "inactive", "blocked"],
353
+ },
354
+ };
355
+ ```
356
+
357
+ Error code: `INVALID_ENUM`
358
+
359
+ Possible error codes for types:
360
+
361
+ ```text
362
+
363
+ INVALID_TYPE
364
+
365
+ INVALID_EMAIL
366
+
367
+ INVALID_URL
368
+
369
+ INVALID_ENUM
370
+
371
+ INVALID_UUID
372
+
373
+ INVALID_UUID_V1
374
+
375
+ INVALID_UUID_V3
376
+
377
+ INVALID_UUID_V4
378
+
379
+ INVALID_UUID_V5
380
+
381
+ INVALID_OBJECT_ID
382
+ ```
383
+
384
+ ---
385
+
386
+ ### `regex`
387
+
388
+ Validates a value using a regular expression.
389
+
390
+ **Default:** Not applied when omitted.
391
+
392
+ Example:
393
+
394
+ ```js
395
+ const rules = {
396
+ employeeCode: {
397
+ type: "string",
398
+
399
+ regex: /^[A-Z]{3}[0-9]{3}$/,
400
+ },
401
+ };
402
+ ```
403
+
404
+ Error code: `REGEX_MISMATCH`
405
+
406
+ ---
407
+
408
+ ### `minLength`
409
+
410
+ Defines the minimum allowed string length.
411
+
412
+ **Default:** Not applied when omitted.
413
+
414
+ Example:
415
+
416
+ ```js
417
+ const rules = {
418
+ username: {
419
+ type: "string",
420
+
421
+ minLength: 5,
422
+ },
423
+ };
424
+ ```
425
+
426
+ Error code: `MIN_LENGTH`
427
+
428
+ ---
429
+
430
+ ### `maxLength`
431
+
432
+ Defines the maximum allowed string length.
433
+
434
+ **Default:** Not applied when omitted.
435
+
436
+ Example:
437
+
438
+ ```js
439
+ const rules = {
440
+ username: {
441
+ type: "string",
442
+
443
+ maxLength: 20,
444
+ },
445
+ };
446
+ ```
447
+
448
+ Error code: `MAX_LENGTH`
449
+
450
+ ---
451
+
452
+ ### `preventDecimal`
453
+
454
+ Prevents decimal numbers.
455
+
456
+ **Default:** `false` both integer and decimal numbers are allowed.
457
+
458
+ Example:
459
+
460
+ ```js
461
+ const rules = {
462
+ quantity: {
463
+ type: "number",
464
+
465
+ preventDecimal: true,
466
+ },
467
+ };
468
+ ```
469
+
470
+ Error code: `DECIMAL_NOT_ALLOWED`
471
+
472
+ ---
473
+
474
+ ### `min`
475
+
476
+ Defines the minimum allowed numeric value.
477
+
478
+ **Default:** Not applied when omitted.
479
+
480
+ Example:
481
+
482
+ ```js
483
+ const rules = {
484
+ age: {
485
+ type: "number",
486
+
487
+ min: 18,
488
+ },
489
+ };
490
+ ```
491
+
492
+ Error code: `MIN_VALUE`
493
+
494
+ ---
495
+
496
+ ### `max`
497
+
498
+ Defines the maximum allowed numeric value.
499
+
500
+ **Default:** Not applied when omitted.
501
+
502
+ Example:
503
+
504
+ ```js
505
+ const rules = {
506
+ quantity: {
507
+ type: "number",
508
+
509
+ max: 100,
510
+ },
511
+ };
512
+ ```
513
+
514
+ Error code: `MAX_VALUE`
515
+
516
+ ---
517
+
518
+ ### `range`
519
+
520
+ Defines the allowed numeric range.
521
+
522
+ **Default:** Not applied when omitted.
523
+
524
+ Example:
525
+
526
+ ```js
527
+ const rules = {
528
+ marks: {
529
+ type: "number",
530
+
531
+ range: "0-100",
532
+ },
533
+ };
534
+ ```
535
+
536
+ Error code: `OUT_OF_RANGE`
537
+
538
+ ---
539
+
540
+ ### `elementConstraints`
541
+
542
+ Validates every item in an array.
543
+
544
+ Example:
545
+
546
+ ```js
547
+ const rules = {
548
+ marks: {
549
+ type: "array",
550
+
551
+ elementConstraints: {
552
+ type: "number",
553
+
554
+ range: "0-100",
555
+ },
556
+ },
557
+ };
558
+ ```
559
+
560
+ Example error:
561
+
562
+ ```js
563
+
564
+ {
565
+
566
+   path: "marks[2]",
567
+
568
+   code: "OUT_OF_RANGE",
569
+
570
+   message:
571
+
572
+     "Attribute marks[2] should have a value between 0 and 100"
573
+
574
+ }
575
+ ```
576
+
577
+ When `elementConstraintsError` is explicitly provided, the error code
578
+ is: `INVALID_ARRAY_ELEMENT`
579
+
580
+ Example:
581
+
582
+ ```js
583
+ const rules = {
584
+ marks: {
585
+ type: "array",
586
+
587
+ elementConstraints: {
588
+ type: "number",
589
+ },
590
+
591
+ elementConstraintsError: "Every marks element must be a number",
592
+ },
593
+ };
594
+ ```
595
+
596
+ ---
597
+
598
+ ### `objectAttr`
599
+
600
+ Validates fields inside a nested object.
601
+
602
+ Example:
603
+
604
+ ```js
605
+ const rules = {
606
+ address: {
607
+ type: "object",
608
+
609
+ objectAttr: {
610
+ city: {
611
+ mandatory: true,
612
+
613
+ type: "string",
614
+ },
615
+
616
+ location: {
617
+ type: "object",
618
+
619
+ objectAttr: {
620
+ latitude: {
621
+ type: "number",
622
+ },
623
+
624
+ longitude: {
625
+ type: "number",
626
+ },
627
+ },
628
+ },
629
+ },
630
+ },
631
+ };
632
+ ```
633
+
634
+ Nested errors include the complete field path:
635
+
636
+ ```js
637
+
638
+ {
639
+
640
+   path: "address.location.latitude",
641
+
642
+   code: "INVALID_TYPE",
643
+
644
+   message:
645
+
646
+     "Invalid type for attribute address.location.latitude, required number value"
647
+
648
+ }
649
+ ```
650
+
651
+ ---
652
+
653
+ ### `dependency`
654
+
655
+ Allows validation rules to depend on another field.
656
+
657
+ Example:
658
+
659
+ ```js
660
+ const rules = {
661
+ minSalary: {
662
+ type: "number",
663
+
664
+ dependency: {
665
+ maxSalary: {
666
+ setDependencyRule: (minSalary, maxSalary) => ({
667
+ type: "number",
668
+
669
+ min: minSalary + 1,
670
+
671
+ minError: "maxSalary must be more than minSalary",
672
+ }),
673
+ },
674
+ },
675
+ },
676
+ };
677
+ ```
678
+
679
+ Example error:
680
+
681
+ ```js
682
+
683
+ {
684
+
685
+   path: "maxSalary",
686
+
687
+   code: "MIN_VALUE",
688
+
689
+   message:
690
+
691
+     "maxSalary must be more than minSalary"
692
+
693
+ }
694
+ ```
695
+
696
+ ## Error Codes
697
+
698
+ `perfectPayload()` currently exposes the following machine-readable
699
+ validation error codes:
700
+
701
+ ```text
702
+
703
+ REQUIRED
704
+
705
+ NULL_NOT_ALLOWED
706
+
707
+ EMPTY_OBJECT_NOT_ALLOWED
708
+
709
+ EMPTY_ARRAY_NOT_ALLOWED
710
+
711
+ INVALID_ARRAY_ELEMENT
712
+
713
+ REGEX_MISMATCH
714
+
715
+ INVALID_TYPE
716
+
717
+ INVALID_EMAIL
718
+
719
+ INVALID_URL
720
+
721
+ INVALID_ENUM
722
+
723
+ INVALID_UUID
724
+
725
+ INVALID_UUID_V1
726
+
727
+ INVALID_UUID_V3
728
+
729
+ INVALID_UUID_V4
730
+
731
+ INVALID_UUID_V5
732
+
733
+ INVALID_OBJECT_ID
734
+
735
+ MIN_LENGTH
736
+
737
+ MAX_LENGTH
738
+
739
+ DECIMAL_NOT_ALLOWED
740
+
741
+ MIN_VALUE
742
+
743
+ MAX_VALUE
744
+
745
+ OUT_OF_RANGE
746
+ ```
747
+
748
+ These codes are designed for programmatic handling while `message`
749
+ remains suitable for human-readable API responses.
750
+
751
+ For example:
752
+
753
+ ```js
754
+ const result = perfectPayload(payload, validationRules);
755
+
756
+ if (!result.valid) {
757
+ const emailError = result.errors.find(
758
+ (error) => error.code === "INVALID_EMAIL",
759
+ );
760
+
761
+ if (emailError) {
762
+ // Handle invalid email
763
+ }
764
+ }
765
+ ```
766
+
767
+ ## Custom Error Messages
768
+
769
+ Every validation rule can use its corresponding custom error message.
770
+
771
+ Custom messages replace the default human-readable `message` while
772
+ keeping the same structured error format:
773
+
774
+ ```js
775
+
776
+ {
777
+
778
+   path: "email",
779
+
780
+   code: "INVALID_EMAIL",
781
+
782
+   message: "Email address is invalid"
783
+
784
+ }
785
+ ```
786
+
787
+ Example:
788
+
789
+ ```js
790
+ const rules = {
791
+ email: {
792
+ mandatory: true,
793
+
794
+ type: "email",
795
+
796
+ mandatoryError: "Email is required",
797
+
798
+ typeError: "Email address is invalid",
799
+ },
800
+ };
801
+ ```
802
+
803
+ If `email` is missing:
804
+
805
+ ```js
806
+
807
+ {
808
+
809
+   path: "email",
810
+
811
+   code: "REQUIRED",
812
+
813
+   message: "Email is required"
814
+
815
+ }
816
+ ```
817
+
818
+ If `email` is present but invalid:
819
+
820
+ ```js
821
+
822
+ {
823
+
824
+   path: "email",
825
+
826
+   code: "INVALID_EMAIL",
827
+
828
+   message: "Email address is invalid"
829
+
830
+ }
831
+ ```
832
+
833
+ ### Supported Custom Error Properties
834
+
835
+ \| Validation Rule      \| Custom Error Property     \|
836
+
837
+ \| -------------------- \| ------------------------- \|
838
+
839
+ \| `mandatory`          \| `mandatoryError`          \|
840
+
841
+ \| `allowNull`          \| `allowNullError`          \|
842
+
843
+ \| `allowEmptyObject`   \| `emptyObjectError`        \|
844
+
845
+ \| `allowEmptyArray`    \| `emptyArrayError`         \|
846
+
847
+ \| `elementConstraints` \| `elementConstraintsError` \|
848
+
849
+ \| `regex`              \| `regexError`              \|
850
+
851
+ \| `type`               \| `typeError`               \|
852
+
853
+ \| `minLength`          \| `minLengthError`          \|
854
+
855
+ \| `maxLength`          \| `maxLengthError`          \|
856
+
857
+ \| `preventDecimal`     \| `preventDecimalError`     \|
858
+
859
+ \| `min`                \| `minError`                \|
860
+
861
+ \| `max`                \| `maxError`                \|
862
+
863
+ \| `range`              \| `rangeError`              \|
864
+
865
+ ### Example with Multiple Custom Errors
866
+
867
+ ```js
868
+ const payload = {
869
+ username: "ab",
870
+
871
+ age: 15,
872
+
873
+ score: 120,
874
+ };
875
+
876
+ const rules = {
877
+ username: {
878
+ mandatory: true,
879
+
880
+ type: "string",
881
+
882
+ minLength: 3,
883
+
884
+ mandatoryError: "Username is required",
885
+
886
+ typeError: "Username must be a string",
887
+
888
+ minLengthError: "Username must contain at least 3 characters",
889
+ },
890
+
891
+ age: {
892
+ type: "number",
893
+
894
+ min: 18,
895
+
896
+ minError: "Age must be at least 18",
897
+ },
898
+
899
+ score: {
900
+ type: "number",
901
+
902
+ range: "0-100",
903
+
904
+ rangeError: "Score must be between 0 and 100",
905
+ },
906
+ };
907
+
908
+ const result = perfectPayload(payload, rules);
909
+ ```
910
+
911
+ Example result:
912
+
913
+ ```js
914
+
915
+ {
916
+
917
+   statusCode: 400,
918
+
919
+   valid: false,
920
+
921
+   message:
922
+
923
+     "One or more attribute values are invalid",
924
+
925
+   errors: [
926
+
927
+     {
928
+
929
+       path: "username",
930
+
931
+       code: "MIN_LENGTH",
932
+
933
+       message:
934
+
935
+         "Username must contain at least 3 characters"
936
+
937
+     },
938
+
939
+     {
940
+
941
+       path: "age",
942
+
943
+       code: "MIN_VALUE",
944
+
945
+       message:
946
+
947
+         "Age must be at least 18"
948
+
949
+     },
950
+
951
+     {
952
+
953
+       path: "score",
954
+
955
+       code: "OUT_OF_RANGE",
956
+
957
+       message:
958
+
959
+         "Score must be between 0 and 100"
960
+
961
+     }
962
+
963
+   ]
964
+
965
+ }
966
+ ```
967
+
968
+ ### Custom Messages and Error Codes
969
+
970
+ Custom messages only replace the `message`.
971
+
972
+ They do not change the validation error `code`.
973
+
974
+ For example:
975
+
976
+ ```js
977
+ const rules = {
978
+ age: {
979
+ type: "number",
980
+
981
+ min: 18,
982
+
983
+ minError: "You must be 18 or older",
984
+ },
985
+ };
986
+ ```
987
+
988
+ Still returns:
989
+
990
+ ```js
991
+
992
+ {
993
+
994
+   path: "age",
995
+
996
+   code: "MIN_VALUE",
997
+
998
+   message: "You must be 18 or older"
999
+
1000
+ }
1001
+ ```
1002
+
1003
+ This makes it possible to:
1004
+
1005
+ - show custom messages to API consumers
1006
+ - use stable error codes in application logic
1007
+ - change user-facing wording without changing programmatic error
1008
+ handling
1009
+
1010
+ ## Custom Response Objects
1011
+
1012
+ `perfectPayload()` allows you to customize both the valid and invalid
1013
+ response objects.
1014
+
1015
+ The third argument is the custom valid response.
1016
+
1017
+ The fourth argument is the custom invalid response.
1018
+
1019
+ ### Custom Valid Response
1020
+
1021
+ Example:
1022
+
1023
+ ```js
1024
+ const customValidResponse = {
1025
+ statusCode: 201,
1026
+
1027
+ valid: true,
1028
+
1029
+ message: "Payload validated successfully",
1030
+ };
1031
+
1032
+ const result = perfectPayload(payload, validationRules, customValidResponse);
1033
+ ```
1034
+
1035
+ When validation succeeds, `validatedPayload` is automatically added:
1036
+
1037
+ ```js
1038
+
1039
+ {
1040
+
1041
+   statusCode: 201,
1042
+
1043
+   valid: true,
1044
+
1045
+   message: "Payload validated successfully",
1046
+
1047
+   validatedPayload: {
1048
+
1049
+     name: "Kiran",
1050
+
1051
+     email: "kiran@example.com",
1052
+
1053
+     age: 29
1054
+
1055
+   }
1056
+
1057
+ }
1058
+ ```
1059
+
1060
+ ### Custom Invalid Response
1061
+
1062
+ Example:
1063
+
1064
+ ```js
1065
+ const customInvalidResponse = {
1066
+ statusCode: 422,
1067
+
1068
+ valid: false,
1069
+
1070
+ message: "Payload validation failed",
1071
+ };
1072
+
1073
+ const result = perfectPayload(
1074
+ payload,
1075
+
1076
+ validationRules,
1077
+
1078
+ undefined,
1079
+
1080
+ customInvalidResponse,
1081
+ );
1082
+ ```
1083
+
1084
+ When validation fails, `errors` is automatically added:
1085
+
1086
+ ```js
1087
+
1088
+ {
1089
+
1090
+   statusCode: 422,
1091
+
1092
+   valid: false,
1093
+
1094
+   message: "Payload validation failed",
1095
+
1096
+   errors: [
1097
+
1098
+     {
1099
+
1100
+       path: "email",
1101
+
1102
+       code: "INVALID_EMAIL",
1103
+
1104
+       message:
1105
+
1106
+         "Invalid email format for attribute email"
1107
+
1108
+     }
1109
+
1110
+   ]
1111
+
1112
+ }
1113
+ ```
1114
+
1115
+ ### Custom Valid and Invalid Responses Together
1116
+
1117
+ ```js
1118
+ const customValidResponse = {
1119
+ statusCode: 201,
1120
+
1121
+ valid: true,
1122
+
1123
+ message: "CUSTOM_VALID_RESPONSE",
1124
+ };
1125
+
1126
+ const customInvalidResponse = {
1127
+ statusCode: 422,
1128
+
1129
+ valid: false,
1130
+
1131
+ message: "CUSTOM_INVALID_RESPONSE",
1132
+ };
1133
+
1134
+ const result = perfectPayload(
1135
+ payload,
1136
+
1137
+ validationRules,
1138
+
1139
+ customValidResponse,
1140
+
1141
+ customInvalidResponse,
1142
+ );
1143
+ ```
1144
+
1145
+ The response object you provide is preserved, while `perfectPayload()`
1146
+ automatically adds either:
1147
+
1148
+ ```text
1149
+
1150
+ validatedPayload
1151
+ ```
1152
+
1153
+ for successful validation, or:
1154
+
1155
+ ```text
1156
+
1157
+ errors
1158
+ ```
1159
+
1160
+ for failed validation.
1161
+
1162
+ ## Default Responses
1163
+
1164
+ If no custom response objects are provided, the default valid response
1165
+ is:
1166
+
1167
+ ```js
1168
+
1169
+ {
1170
+
1171
+   statusCode: 200,
1172
+
1173
+   valid: true,
1174
+
1175
+   validatedPayload: {
1176
+
1177
+     // validated fields
1178
+
1179
+   }
1180
+
1181
+ }
1182
+ ```
1183
+
1184
+ The default invalid response is:
1185
+
1186
+ ```js
1187
+
1188
+ {
1189
+
1190
+   statusCode: 400,
1191
+
1192
+   valid: false,
1193
+
1194
+   message: "One or more attribute values are invalid",
1195
+
1196
+   errors: [
1197
+
1198
+     {
1199
+
1200
+       path: "field",
1201
+
1202
+       code: "ERROR_CODE",
1203
+
1204
+       message: "Validation error message"
1205
+
1206
+     }
1207
+
1208
+   ]
1209
+
1210
+ }
1211
+ ```
1212
+
1213
+ ## Nested Objects and Array Field Paths
1214
+
1215
+ `perfectPayload()` returns the exact location of a validation failure
1216
+ through the `path` property.
1217
+
1218
+ This makes validation errors easier to map to API fields, forms, logs,
1219
+ and frontend components.
1220
+
1221
+ ### Top-Level Field
1222
+
1223
+ For a payload such as:
1224
+
1225
+ ```js
1226
+ const payload = {
1227
+ email: "invalid-email",
1228
+ };
1229
+ ```
1230
+
1231
+ An error can be returned as:
1232
+
1233
+ ```js
1234
+
1235
+ {
1236
+
1237
+   path: "email",
1238
+
1239
+   code: "INVALID_EMAIL",
1240
+
1241
+   message: "Invalid email format for attribute email"
1242
+
1243
+ }
1244
+ ```
1245
+
1246
+ ### Nested Object
1247
+
1248
+ Use `objectAttr` to validate properties inside an object.
1249
+
1250
+ ```js
1251
+ const payload = {
1252
+ address: {
1253
+ city: "Bengaluru",
1254
+
1255
+ location: {
1256
+ latitude: "12.9716",
1257
+
1258
+ longitude: 77.5946,
1259
+ },
1260
+ },
1261
+ };
1262
+
1263
+ const rules = {
1264
+ address: {
1265
+ type: "object",
1266
+
1267
+ objectAttr: {
1268
+ city: {
1269
+ type: "string",
1270
+ },
1271
+
1272
+ location: {
1273
+ type: "object",
1274
+
1275
+ objectAttr: {
1276
+ latitude: {
1277
+ type: "number",
1278
+ },
1279
+
1280
+ longitude: {
1281
+ type: "number",
1282
+ },
1283
+ },
1284
+ },
1285
+ },
1286
+ },
1287
+ };
1288
+
1289
+ const result = perfectPayload(payload, rules);
1290
+ ```
1291
+
1292
+ Because `latitude` is a string instead of a number, the error contains
1293
+ its complete nested path:
1294
+
1295
+ ```js
1296
+
1297
+ {
1298
+
1299
+   path: "address.location.latitude",
1300
+
1301
+   code: "INVALID_TYPE",
1302
+
1303
+   message:
1304
+
1305
+     "Invalid type for attribute address.location.latitude, required number value"
1306
+
1307
+ }
1308
+ ```
1309
+
1310
+ Nested paths use dot notation:
1311
+
1312
+ ```text
1313
+
1314
+ address.city
1315
+
1316
+ address.location.latitude
1317
+
1318
+ address.location.longitude
1319
+ ```
1320
+
1321
+ ### Array Elements
1322
+
1323
+ When `elementConstraints` validation fails, the array index is included
1324
+ in the error path.
1325
+
1326
+ ```js
1327
+ const payload = {
1328
+ marks: [50, 75, 150],
1329
+ };
1330
+
1331
+ const rules = {
1332
+ marks: {
1333
+ type: "array",
1334
+
1335
+ elementConstraints: {
1336
+ type: "number",
1337
+
1338
+ range: "0-100",
1339
+ },
1340
+ },
1341
+ };
1342
+
1343
+ const result = perfectPayload(payload, rules);
1344
+ ```
1345
+
1346
+ The invalid third element is reported as:
1347
+
1348
+ ```js
1349
+
1350
+ {
1351
+
1352
+   path: "marks[2]",
1353
+
1354
+   code: "OUT_OF_RANGE",
1355
+
1356
+   message:
1357
+
1358
+     "Attribute marks[2] should have a value between 0 and 100"
1359
+
1360
+ }
1361
+ ```
1362
+
1363
+ Array paths use zero-based indexes:
1364
+
1365
+ ```text
1366
+
1367
+ marks[0]
1368
+
1369
+ marks[1]
1370
+
1371
+ marks[2]
1372
+ ```
1373
+
1374
+ ### Nested Fields Inside Arrays
1375
+
1376
+ Paths can also identify fields inside array elements.
1377
+
1378
+ For example:
1379
+
1380
+ ```text
1381
+
1382
+ products[0].quantity
1383
+
1384
+ products[1].quantity
1385
+
1386
+ products[2].price
1387
+ ```
1388
+
1389
+ This provides enough information for consumers to identify the exact
1390
+ field that caused the validation error.
1391
+
1392
+ ### Why Structured Paths Are Useful
1393
+
1394
+ Instead of parsing an error message to determine which field failed,
1395
+ applications can directly use:
1396
+
1397
+ ```js
1398
+ error.path;
1399
+ ```
1400
+
1401
+ For example:
1402
+
1403
+ ```js
1404
+ const result = perfectPayload(payload, validationRules);
1405
+
1406
+ if (!result.valid) {
1407
+ result.errors.forEach((error) => {
1408
+ console.log(error.path, error.code, error.message);
1409
+ });
1410
+ }
1411
+ ```
1412
+
1413
+ A frontend can also map validation errors by path:
1414
+
1415
+ ```js
1416
+ const fieldErrors = {};
1417
+
1418
+ result.errors.forEach((error) => {
1419
+ fieldErrors[error.path] = error.message;
1420
+ });
1421
+ ```
1422
+
1423
+ Result:
1424
+
1425
+ ```js
1426
+
1427
+ {
1428
+
1429
+   "email":
1430
+
1431
+     "Invalid email format for attribute email",
1432
+
1433
+   "address.location.latitude":
1434
+
1435
+     "Invalid type for attribute address.location.latitude, required number value",
1436
+
1437
+   "marks[2]":
1438
+
1439
+     "Attribute marks[2] should have a value between 0 and 100"
1440
+
1441
+ }
1442
+ ```
1443
+
1444
+ ## Examples And Usage
1445
+
1446
+ ### Sample Validation Rule
1447
+
1448
+ sample-1
1449
+
1450
+ ```javascript
1451
+
1452
+ {
1453
+
1454
+   firstName: {
1455
+
1456
+     mandatory: true,
1457
+
1458
+     allowNull: false,
1459
+
1460
+     type: "string",
1461
+
1462
+     minLength: 3,
1463
+
1464
+     minLengthError:"First name must have minimum 3 characters."
1465
+
1466
+   },
1467
+
1468
+   lastName: {
1469
+
1470
+     mandatory: false,
1471
+
1472
+     allowNull: true,
1473
+
1474
+     type: "string",
1475
+
1476
+   },
1477
+
1478
+   email: {
1479
+
1480
+     mandatory: true,
1481
+
1482
+     allowNull: false,
1483
+
1484
+     type: "email",
1485
+
1486
+   },
1487
+
1488
+   phone: {
1489
+
1490
+     mandatory: true,
1491
+
1492
+     allowNull: false,
1493
+
1494
+     type: "string",
1495
+
1496
+   },
1497
+
1498
+   age: {
1499
+
1500
+     mandatory: false,
1501
+
1502
+     type: "number",
1503
+
1504
+     min: 1,
1505
+
1506
+     max: 120,
1507
+
1508
+   },
1509
+
1510
+ };
1511
+ ```
1512
+
1513
+ sample-2
1514
+
1515
+ ```javascript
1516
+
1517
+ {
1518
+
1519
+   id: {
1520
+
1521
+     mandatory: true,
1522
+
1523
+     allowNull: true,
1524
+
1525
+     type: "uuidv4",
1526
+
1527
+   },
1528
+
1529
+   batchId: {
1530
+
1531
+     mandatory: true,
1532
+
1533
+     allowNull: true,
1534
+
1535
+     type: "objectId",
1536
+
1537
+   },
1538
+
1539
+   firstName: {
1540
+
1541
+     mandatory: true,
1542
+
1543
+     type: "string",
1544
+
1545
+     minLength: 3,
1546
+
1547
+   },
1548
+
1549
+   lastName: {
1550
+
1551
+     mandatory: false,
1552
+
1553
+     allowNull: true,
1554
+
1555
+     type: "string",
1556
+
1557
+   },
1558
+
1559
+   age: {
1560
+
1561
+     type: "number",
1562
+
1563
+     min: 0.1,
1564
+
1565
+     max: 120,
1566
+
1567
+   },
1568
+
1569
+   isAdult: {
1570
+
1571
+     type: "boolean",
1572
+
1573
+   },
1574
+
1575
+   totalWins: {
1576
+
1577
+     type: "number",
1578
+
1579
+     min: 0,
1580
+
1581
+     preventDecimal: true,
1582
+
1583
+   },
1584
+
1585
+   email: {
1586
+
1587
+     regex: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$/,
1588
+
1589
+   },
1590
+
1591
+   githubLink: {
1592
+
1593
+     type: "url",
1594
+
1595
+   },
1596
+
1597
+   accountStatus: {
1598
+
1599
+     type: "enum",
1600
+
1601
+     enumValues: ["Active", "Inactive", 200],
1602
+
1603
+   },
1604
+
1605
+   marks: {
1606
+
1607
+     range: "0-100",
1608
+
1609
+   },
1610
+
1611
+   allMarks: {
1612
+
1613
+     type: "array",
1614
+
1615
+     allowEmptyArray: false,
1616
+
1617
+     elementConstraints: {
1618
+
1619
+       type: "number",
1620
+
1621
+       allowNull: false,
1622
+
1623
+       range: "0-100",
1624
+
1625
+     },
1626
+
1627
+   },
1628
+
1629
+   totalScore: {
1630
+
1631
+     type: "number",
1632
+
1633
+     dependency: {
1634
+
1635
+       result: {
1636
+
1637
+         setDependencyRule: (totalScore, result) => {
1638
+
1639
+           return { mandatory: true, allowNull: false, type: "string" };
1640
+
1641
+         },
1642
+
1643
+       },
1644
+
1645
+     },
1646
+
1647
+   },
1648
+
1649
+   result: {
1650
+
1651
+     type: "string",
1652
+
1653
+     dependency: {
1654
+
1655
+       totalScore: {
1656
+
1657
+         setDependencyRule: (result, totalScore) => {
1658
+
1659
+           return { mandatory: true, allowNull: false, type: "number" };
1660
+
1661
+         },
1662
+
1663
+       },
1664
+
1665
+     },
1666
+
1667
+   },
1668
+
1669
+   minSalary: {
1670
+
1671
+     mandatory: true,
1672
+
1673
+     min: 1,
1674
+
1675
+     type: "number",
1676
+
1677
+     dependency: {
1678
+
1679
+       maxSalary: {
1680
+
1681
+         setDependencyRule: (minSalary, maxSalary) => {
1682
+
1683
+           return {
1684
+
1685
+             mandatory: true,
1686
+
1687
+             min: minSalary + 1,
1688
+
1689
+             minError: "maxSalary must be more than minSalary",
1690
+
1691
+           };
1692
+
1693
+         },
1694
+
1695
+       },
1696
+
1697
+     },
1698
+
1699
+   },
1700
+
1701
+   maxSalary: {
1702
+
1703
+     dependency: {
1704
+
1705
+       minSalary: {
1706
+
1707
+         setDependencyRule: (maxSalary, minSalary) => {
1708
+
1709
+           return {
1710
+
1711
+             mandatory: true,
1712
+
1713
+             max: maxSalary - 1,
1714
+
1715
+             maxError: "minSalary must be less than maxSalary",
1716
+
1717
+           };
1718
+
1719
+         },
1720
+
1721
+       },
1722
+
1723
+     },
1724
+
1725
+   },
1726
+
1727
+   address: {
1728
+
1729
+     mandatory: true,
1730
+
1731
+     type: "object",
1732
+
1733
+     allowEmptyObject: false,
1734
+
1735
+     objectAttr: {
1736
+
1737
+       country: { mandatory: true, type: "string" },
1738
+
1739
+       state: {
1740
+
1741
+         mandatory: true,
1742
+
1743
+         type: "string",
1744
+
1745
+       },
1746
+
1747
+       city: {},
1748
+
1749
+       zip: {
1750
+
1751
+         mandatory: true,
1752
+
1753
+         type: "string",
1754
+
1755
+       },
1756
+
1757
+       position: {
1758
+
1759
+         mandatory: true,
1760
+
1761
+         type: "object",
1762
+
1763
+         allowEmptyObject: false,
1764
+
1765
+         objectAttr: {
1766
+
1767
+           lattitude: { mandatory: true, type: "number" },
1768
+
1769
+           longitude: {
1770
+
1771
+             mandatory: true,
1772
+
1773
+             type: "number",
1774
+
1775
+           },
1776
+
1777
+         },
1778
+
1779
+       },
1780
+
1781
+     },
1782
+
1783
+   },
1784
+
1785
+ }
1786
+ ```
1787
+
1788
+ ### Usage
1789
+
1790
+ #### creating your route with payload validation middleware
1791
+
1792
+ ```javascript
1793
+
1794
+ //Here validatePayload is your middleware function, where you're invoking perfect payload
1795
+
1796
+ router.post(
1797
+
1798
+   "/payload-validation",
1799
+
1800
+   validatePayload({ rule: <your validation rule json object> }),
1801
+
1802
+   (req, res) => res.send("OK")
1803
+
1804
+ );
1805
+ ```
1806
+
1807
+ #### 1 Use perfect-payload in your middleware like below(for MODULE JS)
1808
+
1809
+ ```javascript
1810
+ import { perfectPayloadV1 } from "perfect-payload";
1811
+
1812
+ export const validatePayload = ({ rule }) => {
1813
+ return (req, res, next) => {
1814
+ try {
1815
+ const { statusCode, ...response } = perfectPayloadV1(req?.body, rule);
1816
+
1817
+ if (+statusCode >= 200 && +statusCode <= 299) {
1818
+ req.validatedBody = response?.validatedPayload;
1819
+
1820
+ next();
1821
+ } else res.status(statusCode).json(response);
1822
+ } catch (error) {
1823
+ console.error("Error validating payload", error);
1824
+
1825
+ res.status(500).json({ error: "Internal Server Error" });
1826
+ }
1827
+ };
1828
+ };
1829
+ ```
1830
+
1831
+ #### 2 Use perfect-payload in your middleware like below(for COMMON JS)
1832
+
1833
+ ```javascript
1834
+ function validatePayload({ rule }) {
1835
+ return async (req, res, next) => {
1836
+ try {
1837
+ const { perfectPayloadV1 } = await import("perfect-payload");
1838
+
1839
+ const { statusCode, ...response } = perfectPayloadV1(req?.body, rule);
1840
+
1841
+ if (+statusCode >= 200 && +statusCode <= 299) {
1842
+ req.validatedBody = response?.validatedPayload;
1843
+
1844
+ next();
1845
+ } else {
1846
+ res.status(statusCode).json(response);
1847
+ }
1848
+ } catch (error) {
1849
+ console.error("Error validating payload", error);
1850
+
1851
+ res.status(500).json({ error: "Internal Server Error" });
1852
+ }
1853
+ };
1854
+ }
1855
+
1856
+ module.exports = { validatePayload };
1857
+ ```
1858
+
1859
+ ---
1860
+
1861
+ This documentation provides a comprehensive guide to using the data
1862
+ validation module effectively. Ensure to define your validation rules
1863
+ clearly to maintain data quality and consistency in your applications.