perfect-payload 1.3.0-beta.2 → 1.4.0-beta.0
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 +589 -377
- package/index.js +99 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,15 +1,32 @@
|
|
|
1
1
|
# perfect-payload
|
|
2
2
|
|
|
3
3
|
A lightweight JavaScript payload validation utility for validating API
|
|
4
|
-
|
|
5
4
|
and JSON payloads with simple rule-based configuration.
|
|
6
5
|
|
|
6
|
+
`perfect-payload` supports structured validation errors, nested field
|
|
7
|
+
paths, synchronous custom validators, and synchronous payload
|
|
8
|
+
transformation/sanitization while keeping the validation schema simple.
|
|
9
|
+
|
|
10
|
+
## Quick Links
|
|
11
|
+
|
|
12
|
+
- [Installation](#installation)
|
|
13
|
+
- [Basic Usage](#basic-usage)
|
|
14
|
+
- [Validation Rules](#validation-rules)
|
|
15
|
+
- [Transformations and
|
|
16
|
+
Sanitization](#transformations-and-sanitization)
|
|
17
|
+
- [Custom Validators](#customvalidator)
|
|
18
|
+
- [Error Codes](#error-codes)
|
|
19
|
+
- [Custom Error Messages](#custom-error-messages)
|
|
20
|
+
- [Nested Objects and Array Field
|
|
21
|
+
Paths](#nested-objects-and-array-field-paths)
|
|
22
|
+
- [Examples and Usage](#examples-and-usage)
|
|
23
|
+
- [Legacy API](#legacy-api)
|
|
24
|
+
|
|
7
25
|
## Installation
|
|
8
26
|
|
|
9
27
|
```bash
|
|
10
28
|
|
|
11
29
|
npm install perfect-payload
|
|
12
|
-
|
|
13
30
|
```
|
|
14
31
|
|
|
15
32
|
## Basic Usage
|
|
@@ -60,25 +77,25 @@ console.log(result);
|
|
|
60
77
|
|
|
61
78
|
{
|
|
62
79
|
|
|
63
|
-
|
|
80
|
+
statusCode: 200,
|
|
64
81
|
|
|
65
|
-
|
|
82
|
+
valid: true,
|
|
66
83
|
|
|
67
|
-
|
|
84
|
+
validatedPayload: {
|
|
68
85
|
|
|
69
|
-
|
|
86
|
+
name: "Kiran",
|
|
70
87
|
|
|
71
|
-
|
|
88
|
+
email: "kiran@example.com",
|
|
72
89
|
|
|
73
|
-
|
|
90
|
+
age: 29
|
|
74
91
|
|
|
75
|
-
|
|
92
|
+
}
|
|
76
93
|
|
|
77
94
|
}
|
|
78
|
-
|
|
79
95
|
```
|
|
80
96
|
|
|
81
|
-
|
|
97
|
+
Note: The validatedPayload contains only the fields
|
|
98
|
+
defined in the
|
|
82
99
|
|
|
83
100
|
schema, automatically filtering out any extra attributes. You can use it
|
|
84
101
|
|
|
@@ -92,28 +109,27 @@ to safely overwrite request.body or assign it to a new request property
|
|
|
92
109
|
|
|
93
110
|
{
|
|
94
111
|
|
|
95
|
-
|
|
112
|
+
statusCode: 400,
|
|
96
113
|
|
|
97
|
-
|
|
114
|
+
valid: false,
|
|
98
115
|
|
|
99
|
-
|
|
116
|
+
message: "One or more attribute values are invalid",
|
|
100
117
|
|
|
101
|
-
|
|
118
|
+
errors: [
|
|
102
119
|
|
|
103
|
-
|
|
120
|
+
{
|
|
104
121
|
|
|
105
|
-
|
|
122
|
+
path: "email",
|
|
106
123
|
|
|
107
|
-
|
|
124
|
+
code: "INVALID_EMAIL",
|
|
108
125
|
|
|
109
|
-
|
|
126
|
+
message: "Invalid email format for attribute email"
|
|
110
127
|
|
|
111
|
-
|
|
128
|
+
}
|
|
112
129
|
|
|
113
|
-
|
|
130
|
+
]
|
|
114
131
|
|
|
115
132
|
}
|
|
116
|
-
|
|
117
133
|
```
|
|
118
134
|
|
|
119
135
|
Each error returned by `perfectPayload()` contains:
|
|
@@ -122,14 +138,13 @@ Each error returned by `perfectPayload()` contains:
|
|
|
122
138
|
|
|
123
139
|
{
|
|
124
140
|
|
|
125
|
-
|
|
141
|
+
path: "field.path",
|
|
126
142
|
|
|
127
|
-
|
|
143
|
+
code: "ERROR_CODE",
|
|
128
144
|
|
|
129
|
-
|
|
145
|
+
message: "Human readable validation message"
|
|
130
146
|
|
|
131
147
|
}
|
|
132
|
-
|
|
133
148
|
```
|
|
134
149
|
|
|
135
150
|
\- `path` identifies the exact field that failed validation.
|
|
@@ -152,7 +167,7 @@ import { perfectPayloadV1 } from "perfect-payload";
|
|
|
152
167
|
|
|
153
168
|
`perfectPayloadV1()` is deprecated and will no longer be supported after
|
|
154
169
|
|
|
155
|
-
|
|
170
|
+
March 31, 2027.
|
|
156
171
|
|
|
157
172
|
Existing applications can continue using it during the migration period,
|
|
158
173
|
|
|
@@ -182,7 +197,8 @@ errors: [
|
|
|
182
197
|
];
|
|
183
198
|
```
|
|
184
199
|
|
|
185
|
-
|
|
200
|
+
Note: If an inValidPayloadResponse is provided, the
|
|
201
|
+
system returns
|
|
186
202
|
|
|
187
203
|
it alongside an automatically generated errors property. Do not include
|
|
188
204
|
|
|
@@ -192,13 +208,14 @@ object.
|
|
|
192
208
|
|
|
193
209
|
## Validation Rules
|
|
194
210
|
|
|
195
|
-
`perfectPayload()` supports
|
|
211
|
+
`perfectPayload()` supports validation, nested-schema,
|
|
212
|
+
custom-validation, and transformation rules.
|
|
196
213
|
|
|
197
214
|
### `mandatory`
|
|
198
215
|
|
|
199
|
-
Marks a field as required
|
|
216
|
+
Marks a field as required. An empty string is also treated as missing.
|
|
200
217
|
|
|
201
|
-
|
|
218
|
+
Default: `false`, the field is not required.
|
|
202
219
|
|
|
203
220
|
```js
|
|
204
221
|
const rules = {
|
|
@@ -210,13 +227,13 @@ const rules = {
|
|
|
210
227
|
|
|
211
228
|
Error code: `REQUIRED`
|
|
212
229
|
|
|
213
|
-
|
|
230
|
+
---
|
|
214
231
|
|
|
215
232
|
### `allowNull`
|
|
216
233
|
|
|
217
234
|
Controls whether `null` values are accepted.
|
|
218
235
|
|
|
219
|
-
|
|
236
|
+
Default: `true`, `null` values are allowed.
|
|
220
237
|
|
|
221
238
|
Example:
|
|
222
239
|
|
|
@@ -230,13 +247,13 @@ const rules = {
|
|
|
230
247
|
|
|
231
248
|
Error code: `NULL_NOT_ALLOWED`
|
|
232
249
|
|
|
233
|
-
|
|
250
|
+
---
|
|
234
251
|
|
|
235
252
|
### `allowEmptyObject`
|
|
236
253
|
|
|
237
254
|
Controls whether an empty object `{}` is accepted.
|
|
238
255
|
|
|
239
|
-
|
|
256
|
+
Default: `true`, empty objects are allowed.
|
|
240
257
|
|
|
241
258
|
Example:
|
|
242
259
|
|
|
@@ -252,13 +269,13 @@ const rules = {
|
|
|
252
269
|
|
|
253
270
|
Error code: `EMPTY_OBJECT_NOT_ALLOWED`
|
|
254
271
|
|
|
255
|
-
|
|
272
|
+
---
|
|
256
273
|
|
|
257
274
|
### `allowEmptyArray`
|
|
258
275
|
|
|
259
276
|
Controls whether an empty array `[]` is accepted.
|
|
260
277
|
|
|
261
|
-
|
|
278
|
+
Default: `true`, empty arrays are allowed.
|
|
262
279
|
|
|
263
280
|
Example:
|
|
264
281
|
|
|
@@ -274,7 +291,7 @@ const rules = {
|
|
|
274
291
|
|
|
275
292
|
Error code: `EMPTY_ARRAY_NOT_ALLOWED`
|
|
276
293
|
|
|
277
|
-
|
|
294
|
+
---
|
|
278
295
|
|
|
279
296
|
### `type`
|
|
280
297
|
|
|
@@ -311,7 +328,6 @@ objectId
|
|
|
311
328
|
array
|
|
312
329
|
|
|
313
330
|
object
|
|
314
|
-
|
|
315
331
|
```
|
|
316
332
|
|
|
317
333
|
Example:
|
|
@@ -397,16 +413,15 @@ INVALID_UUID_V4
|
|
|
397
413
|
INVALID_UUID_V5
|
|
398
414
|
|
|
399
415
|
INVALID_OBJECT_ID
|
|
400
|
-
|
|
401
416
|
```
|
|
402
417
|
|
|
403
|
-
|
|
418
|
+
---
|
|
404
419
|
|
|
405
420
|
### `regex`
|
|
406
421
|
|
|
407
422
|
Validates a value using a regular expression.
|
|
408
423
|
|
|
409
|
-
|
|
424
|
+
Default: Not applied when omitted.
|
|
410
425
|
|
|
411
426
|
Example:
|
|
412
427
|
|
|
@@ -422,13 +437,13 @@ const rules = {
|
|
|
422
437
|
|
|
423
438
|
Error code: `REGEX_MISMATCH`
|
|
424
439
|
|
|
425
|
-
|
|
440
|
+
---
|
|
426
441
|
|
|
427
442
|
### `minLength`
|
|
428
443
|
|
|
429
444
|
Defines the minimum allowed string length.
|
|
430
445
|
|
|
431
|
-
|
|
446
|
+
Default: Not applied when omitted.
|
|
432
447
|
|
|
433
448
|
Example:
|
|
434
449
|
|
|
@@ -444,13 +459,13 @@ const rules = {
|
|
|
444
459
|
|
|
445
460
|
Error code: `MIN_LENGTH`
|
|
446
461
|
|
|
447
|
-
|
|
462
|
+
---
|
|
448
463
|
|
|
449
464
|
### `maxLength`
|
|
450
465
|
|
|
451
466
|
Defines the maximum allowed string length.
|
|
452
467
|
|
|
453
|
-
|
|
468
|
+
Default: Not applied when omitted.
|
|
454
469
|
|
|
455
470
|
Example:
|
|
456
471
|
|
|
@@ -466,13 +481,14 @@ const rules = {
|
|
|
466
481
|
|
|
467
482
|
Error code: `MAX_LENGTH`
|
|
468
483
|
|
|
469
|
-
|
|
484
|
+
---
|
|
470
485
|
|
|
471
486
|
### `preventDecimal`
|
|
472
487
|
|
|
473
488
|
Prevents decimal numbers.
|
|
474
489
|
|
|
475
|
-
|
|
490
|
+
Default: `false`; both integer and decimal numbers are
|
|
491
|
+
allowed.
|
|
476
492
|
|
|
477
493
|
Example:
|
|
478
494
|
|
|
@@ -488,13 +504,13 @@ const rules = {
|
|
|
488
504
|
|
|
489
505
|
Error code: `DECIMAL_NOT_ALLOWED`
|
|
490
506
|
|
|
491
|
-
|
|
507
|
+
---
|
|
492
508
|
|
|
493
509
|
### `min`
|
|
494
510
|
|
|
495
511
|
Defines the minimum allowed numeric value.
|
|
496
512
|
|
|
497
|
-
|
|
513
|
+
Default: Not applied when omitted.
|
|
498
514
|
|
|
499
515
|
Example:
|
|
500
516
|
|
|
@@ -510,13 +526,13 @@ const rules = {
|
|
|
510
526
|
|
|
511
527
|
Error code: `MIN_VALUE`
|
|
512
528
|
|
|
513
|
-
|
|
529
|
+
---
|
|
514
530
|
|
|
515
531
|
### `max`
|
|
516
532
|
|
|
517
533
|
Defines the maximum allowed numeric value.
|
|
518
534
|
|
|
519
|
-
|
|
535
|
+
Default: Not applied when omitted.
|
|
520
536
|
|
|
521
537
|
Example:
|
|
522
538
|
|
|
@@ -532,13 +548,13 @@ const rules = {
|
|
|
532
548
|
|
|
533
549
|
Error code: `MAX_VALUE`
|
|
534
550
|
|
|
535
|
-
|
|
551
|
+
---
|
|
536
552
|
|
|
537
553
|
### `range`
|
|
538
554
|
|
|
539
555
|
Defines the allowed numeric range.
|
|
540
556
|
|
|
541
|
-
|
|
557
|
+
Default: Not applied when omitted.
|
|
542
558
|
|
|
543
559
|
Example:
|
|
544
560
|
|
|
@@ -554,7 +570,7 @@ const rules = {
|
|
|
554
570
|
|
|
555
571
|
Error code: `OUT_OF_RANGE`
|
|
556
572
|
|
|
557
|
-
|
|
573
|
+
---
|
|
558
574
|
|
|
559
575
|
### `elementConstraints`
|
|
560
576
|
|
|
@@ -582,16 +598,15 @@ Example error:
|
|
|
582
598
|
|
|
583
599
|
{
|
|
584
600
|
|
|
585
|
-
|
|
601
|
+
path: "marks[2]",
|
|
586
602
|
|
|
587
|
-
|
|
603
|
+
code: "OUT_OF_RANGE",
|
|
588
604
|
|
|
589
|
-
|
|
605
|
+
message:
|
|
590
606
|
|
|
591
|
-
|
|
607
|
+
"Attribute marks[2] should have a value between 0 and 100"
|
|
592
608
|
|
|
593
609
|
}
|
|
594
|
-
|
|
595
610
|
```
|
|
596
611
|
|
|
597
612
|
When `elementConstraintsError` is explicitly provided, the error code
|
|
@@ -614,7 +629,7 @@ const rules = {
|
|
|
614
629
|
};
|
|
615
630
|
```
|
|
616
631
|
|
|
617
|
-
|
|
632
|
+
---
|
|
618
633
|
|
|
619
634
|
### `objectAttr`
|
|
620
635
|
|
|
@@ -658,19 +673,18 @@ Nested errors include the complete field path:
|
|
|
658
673
|
|
|
659
674
|
{
|
|
660
675
|
|
|
661
|
-
|
|
676
|
+
path: "address.location.latitude",
|
|
662
677
|
|
|
663
|
-
|
|
678
|
+
code: "INVALID_TYPE",
|
|
664
679
|
|
|
665
|
-
|
|
680
|
+
message:
|
|
666
681
|
|
|
667
|
-
|
|
682
|
+
"Invalid type for attribute address.location.latitude, required number value"
|
|
668
683
|
|
|
669
684
|
}
|
|
670
|
-
|
|
671
685
|
```
|
|
672
686
|
|
|
673
|
-
|
|
687
|
+
---
|
|
674
688
|
|
|
675
689
|
### `dependency`
|
|
676
690
|
|
|
@@ -704,25 +718,221 @@ Example error:
|
|
|
704
718
|
|
|
705
719
|
{
|
|
706
720
|
|
|
707
|
-
|
|
721
|
+
path: "maxSalary",
|
|
708
722
|
|
|
709
|
-
|
|
723
|
+
code: "MIN_VALUE",
|
|
710
724
|
|
|
711
|
-
|
|
725
|
+
message:
|
|
712
726
|
|
|
713
|
-
|
|
727
|
+
"maxSalary must be more than minSalary"
|
|
714
728
|
|
|
715
729
|
}
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
---
|
|
733
|
+
|
|
734
|
+
### Transformations and Sanitization
|
|
735
|
+
|
|
736
|
+
`perfectPayload()` can transform a field before its validation rules
|
|
737
|
+
run. The transformed value is returned in `validatedPayload`, while the
|
|
738
|
+
original input object is not mutated.
|
|
739
|
+
|
|
740
|
+
Supported transformation rules:
|
|
741
|
+
|
|
742
|
+
Rule Purpose
|
|
743
|
+
|
|
744
|
+
---
|
|
745
|
+
|
|
746
|
+
`trim` Removes leading and trailing whitespace from strings
|
|
747
|
+
`lowercase` Converts strings to lowercase
|
|
748
|
+
`uppercase` Converts strings to uppercase
|
|
749
|
+
`transform` Runs a custom synchronous transformation function
|
|
750
|
+
|
|
751
|
+
Transformations always run in this fixed order, regardless of the order
|
|
752
|
+
in which the rule properties are written:
|
|
753
|
+
|
|
754
|
+
```text
|
|
755
|
+
trim
|
|
756
|
+
↓
|
|
757
|
+
lowercase
|
|
758
|
+
↓
|
|
759
|
+
uppercase
|
|
760
|
+
↓
|
|
761
|
+
transform(value, payload)
|
|
762
|
+
↓
|
|
763
|
+
validation rules
|
|
764
|
+
↓
|
|
765
|
+
customValidator
|
|
766
|
+
↓
|
|
767
|
+
validatedPayload
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
#### `trim`
|
|
771
|
+
|
|
772
|
+
```js
|
|
773
|
+
const payload = {
|
|
774
|
+
name: " Kiran Poojary ",
|
|
775
|
+
};
|
|
776
|
+
|
|
777
|
+
const rules = {
|
|
778
|
+
name: {
|
|
779
|
+
type: "string",
|
|
780
|
+
trim: true,
|
|
781
|
+
},
|
|
782
|
+
};
|
|
783
|
+
|
|
784
|
+
const result = perfectPayload(payload, rules);
|
|
785
|
+
|
|
786
|
+
console.log(result.validatedPayload.name);
|
|
787
|
+
// "Kiran Poojary"
|
|
788
|
+
|
|
789
|
+
console.log(payload.name);
|
|
790
|
+
// " Kiran Poojary "
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
`trim` applies only to string values. Non-string values are left
|
|
794
|
+
unchanged.
|
|
795
|
+
|
|
796
|
+
#### `lowercase`
|
|
797
|
+
|
|
798
|
+
```js
|
|
799
|
+
const rules = {
|
|
800
|
+
email: {
|
|
801
|
+
trim: true,
|
|
802
|
+
lowercase: true,
|
|
803
|
+
type: "email",
|
|
804
|
+
},
|
|
805
|
+
};
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
For `" KIRAN@EXAMPLE.COM "`, the validated value becomes
|
|
809
|
+
`"kiran@example.com"`.
|
|
810
|
+
|
|
811
|
+
#### `uppercase`
|
|
812
|
+
|
|
813
|
+
```js
|
|
814
|
+
const rules = {
|
|
815
|
+
countryCode: {
|
|
816
|
+
type: "string",
|
|
817
|
+
uppercase: true,
|
|
818
|
+
},
|
|
819
|
+
};
|
|
820
|
+
```
|
|
821
|
+
|
|
822
|
+
For `"in"`, the validated value becomes `"IN"`.
|
|
823
|
+
|
|
824
|
+
`lowercase: true` and `uppercase: true` cannot be enabled together for
|
|
825
|
+
the same field. Doing so throws a schema configuration error.
|
|
826
|
+
|
|
827
|
+
#### `transform`
|
|
828
|
+
|
|
829
|
+
Use `transform` when the built-in string transformations are not enough.
|
|
830
|
+
|
|
831
|
+
```js
|
|
832
|
+
const rules = {
|
|
833
|
+
phone: {
|
|
834
|
+
type: "string",
|
|
835
|
+
transform: (value) => value.replace(/\s+/g, ""),
|
|
836
|
+
},
|
|
837
|
+
};
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
For `"98765 43210"`, the validated value becomes `"9876543210"`.
|
|
841
|
+
|
|
842
|
+
The transformer receives two arguments:
|
|
843
|
+
|
|
844
|
+
```js
|
|
845
|
+
transform: (value, payload) => {
|
|
846
|
+
return value;
|
|
847
|
+
};
|
|
848
|
+
```
|
|
849
|
+
|
|
850
|
+
- `value` is the field value after the built-in transformations have
|
|
851
|
+
run.
|
|
852
|
+
- `payload` is the current payload/object being validated.
|
|
853
|
+
|
|
854
|
+
This makes cross-field transformations possible:
|
|
855
|
+
|
|
856
|
+
```js
|
|
857
|
+
const payload = {
|
|
858
|
+
amount: 100,
|
|
859
|
+
multiplier: 2,
|
|
860
|
+
};
|
|
861
|
+
|
|
862
|
+
const rules = {
|
|
863
|
+
amount: {
|
|
864
|
+
transform: (value, payload) => value * payload.multiplier,
|
|
865
|
+
type: "number",
|
|
866
|
+
},
|
|
867
|
+
multiplier: {
|
|
868
|
+
type: "number",
|
|
869
|
+
},
|
|
870
|
+
};
|
|
871
|
+
|
|
872
|
+
const result = perfectPayload(payload, rules);
|
|
873
|
+
|
|
874
|
+
console.log(result.validatedPayload.amount);
|
|
875
|
+
// 200
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
A custom transformer may also change the data type before validation:
|
|
879
|
+
|
|
880
|
+
```js
|
|
881
|
+
const rules = {
|
|
882
|
+
quantity: {
|
|
883
|
+
transform: (value) => Number(value),
|
|
884
|
+
type: "number",
|
|
885
|
+
min: 1,
|
|
886
|
+
max: 100,
|
|
887
|
+
},
|
|
888
|
+
};
|
|
889
|
+
```
|
|
716
890
|
|
|
891
|
+
The transformed value is validated by the normal validation rules and is
|
|
892
|
+
also the value received by `customValidator`.
|
|
893
|
+
|
|
894
|
+
Transformations work inside `objectAttr` and `elementConstraints`, and
|
|
895
|
+
transformed nested/array values are preserved in `validatedPayload`.
|
|
896
|
+
|
|
897
|
+
```js
|
|
898
|
+
const rules = {
|
|
899
|
+
profile: {
|
|
900
|
+
type: "object",
|
|
901
|
+
objectAttr: {
|
|
902
|
+
name: {
|
|
903
|
+
trim: true,
|
|
904
|
+
uppercase: true,
|
|
905
|
+
type: "string",
|
|
906
|
+
},
|
|
907
|
+
},
|
|
908
|
+
},
|
|
909
|
+
tags: {
|
|
910
|
+
type: "array",
|
|
911
|
+
elementConstraints: {
|
|
912
|
+
trim: true,
|
|
913
|
+
lowercase: true,
|
|
914
|
+
type: "string",
|
|
915
|
+
},
|
|
916
|
+
},
|
|
917
|
+
};
|
|
717
918
|
```
|
|
718
919
|
|
|
719
|
-
|
|
920
|
+
Missing optional fields are not transformed. An input value of `null` is
|
|
921
|
+
not passed to transformation functions; null handling remains controlled
|
|
922
|
+
by `allowNull`.
|
|
923
|
+
|
|
924
|
+
**Important:** `transform` is synchronous. A non-function transformer,
|
|
925
|
+
an `async` transformer, or a transformer that returns a Promise is not
|
|
926
|
+
supported and throws an error. Exceptions thrown inside the transformer
|
|
927
|
+
propagate to the caller.
|
|
720
928
|
|
|
721
929
|
### `customValidator`
|
|
722
930
|
|
|
723
|
-
Allows you to define custom synchronous validation logic for a field
|
|
931
|
+
Allows you to define custom synchronous validation logic for a field
|
|
932
|
+
when the built-in validation rules are not enough.
|
|
724
933
|
|
|
725
|
-
The validator receives the field value and the
|
|
934
|
+
The validator receives the field value and the current payload/object
|
|
935
|
+
being validated:
|
|
726
936
|
|
|
727
937
|
```js
|
|
728
938
|
customValidator: (value, payload) => {
|
|
@@ -730,7 +940,8 @@ customValidator: (value, payload) => {
|
|
|
730
940
|
};
|
|
731
941
|
```
|
|
732
942
|
|
|
733
|
-
The validator must return `true` to pass validation. Any other return
|
|
943
|
+
The validator must return `true` to pass validation. Any other return
|
|
944
|
+
value causes validation to fail.
|
|
734
945
|
|
|
735
946
|
Example:
|
|
736
947
|
|
|
@@ -738,11 +949,15 @@ Example:
|
|
|
738
949
|
const rules = {
|
|
739
950
|
username: {
|
|
740
951
|
mandatory: true,
|
|
952
|
+
|
|
741
953
|
type: "string",
|
|
954
|
+
|
|
742
955
|
customValidator: (value) => {
|
|
743
956
|
return !value.toLowerCase().includes("admin");
|
|
744
957
|
},
|
|
958
|
+
|
|
745
959
|
customValidatorCode: "RESERVED_USERNAME",
|
|
960
|
+
|
|
746
961
|
customValidatorError: "Username cannot contain admin",
|
|
747
962
|
},
|
|
748
963
|
};
|
|
@@ -759,44 +974,64 @@ const payload = {
|
|
|
759
974
|
The validation error is:
|
|
760
975
|
|
|
761
976
|
```js
|
|
977
|
+
|
|
762
978
|
{
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
979
|
+
|
|
980
|
+
path: "username",
|
|
981
|
+
|
|
982
|
+
code: "RESERVED_USERNAME",
|
|
983
|
+
|
|
984
|
+
message: "Username cannot contain admin"
|
|
985
|
+
|
|
766
986
|
}
|
|
767
987
|
```
|
|
768
988
|
|
|
769
|
-
If `customValidatorCode` and `customValidatorError` are not provided,
|
|
989
|
+
If `customValidatorCode` and `customValidatorError` are not provided,
|
|
990
|
+
the default error is:
|
|
770
991
|
|
|
771
992
|
```js
|
|
993
|
+
|
|
772
994
|
{
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
995
|
+
|
|
996
|
+
path: "username",
|
|
997
|
+
|
|
998
|
+
code: "CUSTOM_VALIDATION_FAILED",
|
|
999
|
+
|
|
1000
|
+
message: "Custom validation failed for attribute username"
|
|
1001
|
+
|
|
776
1002
|
}
|
|
777
1003
|
```
|
|
778
1004
|
|
|
779
|
-
The
|
|
1005
|
+
The current payload/object being validated can be used as the second
|
|
1006
|
+
argument when required:
|
|
780
1007
|
|
|
781
1008
|
```js
|
|
782
1009
|
const rules = {
|
|
783
1010
|
limit: {
|
|
784
1011
|
type: "number",
|
|
785
1012
|
},
|
|
1013
|
+
|
|
786
1014
|
amount: {
|
|
787
1015
|
type: "number",
|
|
1016
|
+
|
|
788
1017
|
customValidator: (value, payload) => {
|
|
789
1018
|
return value <= payload.limit;
|
|
790
1019
|
},
|
|
1020
|
+
|
|
791
1021
|
customValidatorCode: "LIMIT_EXCEEDED",
|
|
1022
|
+
|
|
792
1023
|
customValidatorError: "Amount cannot exceed limit",
|
|
793
1024
|
},
|
|
794
1025
|
};
|
|
795
1026
|
```
|
|
796
1027
|
|
|
797
|
-
`customValidator` also works with nested objects and array
|
|
1028
|
+
`customValidator` also works with nested objects and array
|
|
1029
|
+
`elementConstraints`. The generated structured error automatically
|
|
1030
|
+
contains the corresponding nested or array path.
|
|
798
1031
|
|
|
799
|
-
|
|
1032
|
+
Important: `customValidator` is synchronous. An `async`
|
|
1033
|
+
validator or a validator that returns a Promise is not supported and
|
|
1034
|
+
throws an error. Asynchronous validation is not part of this feature.
|
|
800
1035
|
|
|
801
1036
|
Error code when no custom code is provided: `CUSTOM_VALIDATION_FAILED`
|
|
802
1037
|
|
|
@@ -851,7 +1086,6 @@ MIN_VALUE
|
|
|
851
1086
|
MAX_VALUE
|
|
852
1087
|
|
|
853
1088
|
OUT_OF_RANGE
|
|
854
|
-
|
|
855
1089
|
```
|
|
856
1090
|
|
|
857
1091
|
These codes are designed for programmatic handling while `message`
|
|
@@ -866,20 +1100,19 @@ const result = perfectPayload(payload, validationRules);
|
|
|
866
1100
|
|
|
867
1101
|
if (!result.valid) {
|
|
868
1102
|
|
|
869
|
-
|
|
1103
|
+
const emailError = result.errors.find(
|
|
870
1104
|
|
|
871
|
-
|
|
1105
|
+
(error) => error.code === "INVALID_EMAIL",
|
|
872
1106
|
|
|
873
|
-
|
|
1107
|
+
);
|
|
874
1108
|
|
|
875
|
-
|
|
1109
|
+
if (emailError) {
|
|
876
1110
|
|
|
877
|
-
|
|
1111
|
+
**// Handle invalid email**
|
|
878
1112
|
|
|
879
|
-
|
|
1113
|
+
}
|
|
880
1114
|
|
|
881
1115
|
}
|
|
882
|
-
|
|
883
1116
|
```
|
|
884
1117
|
|
|
885
1118
|
## Custom Error Messages
|
|
@@ -894,14 +1127,13 @@ keeping the same structured error format:
|
|
|
894
1127
|
|
|
895
1128
|
{
|
|
896
1129
|
|
|
897
|
-
|
|
1130
|
+
path: "email",
|
|
898
1131
|
|
|
899
|
-
|
|
1132
|
+
code: "INVALID_EMAIL",
|
|
900
1133
|
|
|
901
|
-
|
|
1134
|
+
message: "Email address is invalid"
|
|
902
1135
|
|
|
903
1136
|
}
|
|
904
|
-
|
|
905
1137
|
```
|
|
906
1138
|
|
|
907
1139
|
Example:
|
|
@@ -926,14 +1158,13 @@ If `email` is missing:
|
|
|
926
1158
|
|
|
927
1159
|
{
|
|
928
1160
|
|
|
929
|
-
|
|
1161
|
+
path: "email",
|
|
930
1162
|
|
|
931
|
-
|
|
1163
|
+
code: "REQUIRED",
|
|
932
1164
|
|
|
933
|
-
|
|
1165
|
+
message: "Email is required"
|
|
934
1166
|
|
|
935
1167
|
}
|
|
936
|
-
|
|
937
1168
|
```
|
|
938
1169
|
|
|
939
1170
|
If `email` is present but invalid:
|
|
@@ -942,14 +1173,13 @@ If `email` is present but invalid:
|
|
|
942
1173
|
|
|
943
1174
|
{
|
|
944
1175
|
|
|
945
|
-
|
|
1176
|
+
path: "email",
|
|
946
1177
|
|
|
947
|
-
|
|
1178
|
+
code: "INVALID_EMAIL",
|
|
948
1179
|
|
|
949
|
-
|
|
1180
|
+
message: "Email address is invalid"
|
|
950
1181
|
|
|
951
1182
|
}
|
|
952
|
-
|
|
953
1183
|
```
|
|
954
1184
|
|
|
955
1185
|
### Supported Custom Error Properties
|
|
@@ -1036,56 +1266,55 @@ Example result:
|
|
|
1036
1266
|
|
|
1037
1267
|
{
|
|
1038
1268
|
|
|
1039
|
-
|
|
1269
|
+
statusCode: 400,
|
|
1040
1270
|
|
|
1041
|
-
|
|
1271
|
+
valid: false,
|
|
1042
1272
|
|
|
1043
|
-
|
|
1273
|
+
message:
|
|
1044
1274
|
|
|
1045
|
-
|
|
1275
|
+
"One or more attribute values are invalid",
|
|
1046
1276
|
|
|
1047
|
-
|
|
1277
|
+
errors: [
|
|
1048
1278
|
|
|
1049
|
-
|
|
1279
|
+
{
|
|
1050
1280
|
|
|
1051
|
-
|
|
1281
|
+
path: "username",
|
|
1052
1282
|
|
|
1053
|
-
|
|
1283
|
+
code: "MIN_LENGTH",
|
|
1054
1284
|
|
|
1055
|
-
|
|
1285
|
+
message:
|
|
1056
1286
|
|
|
1057
|
-
|
|
1287
|
+
"Username must contain at least 3 characters"
|
|
1058
1288
|
|
|
1059
|
-
|
|
1289
|
+
},
|
|
1060
1290
|
|
|
1061
|
-
|
|
1291
|
+
{
|
|
1062
1292
|
|
|
1063
|
-
|
|
1293
|
+
path: "age",
|
|
1064
1294
|
|
|
1065
|
-
|
|
1295
|
+
code: "MIN_VALUE",
|
|
1066
1296
|
|
|
1067
|
-
|
|
1297
|
+
message:
|
|
1068
1298
|
|
|
1069
|
-
|
|
1299
|
+
"Age must be at least 18"
|
|
1070
1300
|
|
|
1071
|
-
|
|
1301
|
+
},
|
|
1072
1302
|
|
|
1073
|
-
|
|
1303
|
+
{
|
|
1074
1304
|
|
|
1075
|
-
|
|
1305
|
+
path: "score",
|
|
1076
1306
|
|
|
1077
|
-
|
|
1307
|
+
code: "OUT_OF_RANGE",
|
|
1078
1308
|
|
|
1079
|
-
|
|
1309
|
+
message:
|
|
1080
1310
|
|
|
1081
|
-
|
|
1311
|
+
"Score must be between 0 and 100"
|
|
1082
1312
|
|
|
1083
|
-
|
|
1313
|
+
}
|
|
1084
1314
|
|
|
1085
|
-
|
|
1315
|
+
]
|
|
1086
1316
|
|
|
1087
1317
|
}
|
|
1088
|
-
|
|
1089
1318
|
```
|
|
1090
1319
|
|
|
1091
1320
|
### Custom Messages and Error Codes
|
|
@@ -1114,14 +1343,13 @@ Still returns:
|
|
|
1114
1343
|
|
|
1115
1344
|
{
|
|
1116
1345
|
|
|
1117
|
-
|
|
1346
|
+
path: "age",
|
|
1118
1347
|
|
|
1119
|
-
|
|
1348
|
+
code: "MIN_VALUE",
|
|
1120
1349
|
|
|
1121
|
-
|
|
1350
|
+
message: "You must be 18 or older"
|
|
1122
1351
|
|
|
1123
1352
|
}
|
|
1124
|
-
|
|
1125
1353
|
```
|
|
1126
1354
|
|
|
1127
1355
|
This makes it possible to:
|
|
@@ -1166,24 +1394,23 @@ When validation succeeds, `validatedPayload` is automatically added:
|
|
|
1166
1394
|
|
|
1167
1395
|
{
|
|
1168
1396
|
|
|
1169
|
-
|
|
1397
|
+
statusCode: 201,
|
|
1170
1398
|
|
|
1171
|
-
|
|
1399
|
+
valid: true,
|
|
1172
1400
|
|
|
1173
|
-
|
|
1401
|
+
message: "Payload validated successfully",
|
|
1174
1402
|
|
|
1175
|
-
|
|
1403
|
+
validatedPayload: {
|
|
1176
1404
|
|
|
1177
|
-
|
|
1405
|
+
name: "Kiran",
|
|
1178
1406
|
|
|
1179
|
-
|
|
1407
|
+
email: "kiran@example.com",
|
|
1180
1408
|
|
|
1181
|
-
|
|
1409
|
+
age: 29
|
|
1182
1410
|
|
|
1183
|
-
|
|
1411
|
+
}
|
|
1184
1412
|
|
|
1185
1413
|
}
|
|
1186
|
-
|
|
1187
1414
|
```
|
|
1188
1415
|
|
|
1189
1416
|
### Custom Invalid Response
|
|
@@ -1216,30 +1443,29 @@ When validation fails, `errors` is automatically added:
|
|
|
1216
1443
|
|
|
1217
1444
|
{
|
|
1218
1445
|
|
|
1219
|
-
|
|
1446
|
+
statusCode: 422,
|
|
1220
1447
|
|
|
1221
|
-
|
|
1448
|
+
valid: false,
|
|
1222
1449
|
|
|
1223
|
-
|
|
1450
|
+
message: "Payload validation failed",
|
|
1224
1451
|
|
|
1225
|
-
|
|
1452
|
+
errors: [
|
|
1226
1453
|
|
|
1227
|
-
|
|
1454
|
+
{
|
|
1228
1455
|
|
|
1229
|
-
|
|
1456
|
+
path: "email",
|
|
1230
1457
|
|
|
1231
|
-
|
|
1458
|
+
code: "INVALID_EMAIL",
|
|
1232
1459
|
|
|
1233
|
-
|
|
1460
|
+
message:
|
|
1234
1461
|
|
|
1235
|
-
|
|
1462
|
+
"Invalid email format for attribute email"
|
|
1236
1463
|
|
|
1237
|
-
|
|
1464
|
+
}
|
|
1238
1465
|
|
|
1239
|
-
|
|
1466
|
+
]
|
|
1240
1467
|
|
|
1241
1468
|
}
|
|
1242
|
-
|
|
1243
1469
|
```
|
|
1244
1470
|
|
|
1245
1471
|
### Custom Valid and Invalid Responses Together
|
|
@@ -1279,7 +1505,6 @@ automatically adds either:
|
|
|
1279
1505
|
```text
|
|
1280
1506
|
|
|
1281
1507
|
validatedPayload
|
|
1282
|
-
|
|
1283
1508
|
```
|
|
1284
1509
|
|
|
1285
1510
|
for successful validation, or:
|
|
@@ -1287,7 +1512,6 @@ for successful validation, or:
|
|
|
1287
1512
|
```text
|
|
1288
1513
|
|
|
1289
1514
|
errors
|
|
1290
|
-
|
|
1291
1515
|
```
|
|
1292
1516
|
|
|
1293
1517
|
for failed validation.
|
|
@@ -1302,18 +1526,17 @@ is:
|
|
|
1302
1526
|
|
|
1303
1527
|
{
|
|
1304
1528
|
|
|
1305
|
-
|
|
1529
|
+
statusCode: 200,
|
|
1306
1530
|
|
|
1307
|
-
|
|
1531
|
+
valid: true,
|
|
1308
1532
|
|
|
1309
|
-
|
|
1533
|
+
validatedPayload: {
|
|
1310
1534
|
|
|
1311
|
-
|
|
1535
|
+
**// validated fields**
|
|
1312
1536
|
|
|
1313
|
-
|
|
1537
|
+
}
|
|
1314
1538
|
|
|
1315
1539
|
}
|
|
1316
|
-
|
|
1317
1540
|
```
|
|
1318
1541
|
|
|
1319
1542
|
The default invalid response is:
|
|
@@ -1322,28 +1545,27 @@ The default invalid response is:
|
|
|
1322
1545
|
|
|
1323
1546
|
{
|
|
1324
1547
|
|
|
1325
|
-
|
|
1548
|
+
statusCode: 400,
|
|
1326
1549
|
|
|
1327
|
-
|
|
1550
|
+
valid: false,
|
|
1328
1551
|
|
|
1329
|
-
|
|
1552
|
+
message: "One or more attribute values are invalid",
|
|
1330
1553
|
|
|
1331
|
-
|
|
1554
|
+
errors: [
|
|
1332
1555
|
|
|
1333
|
-
|
|
1556
|
+
{
|
|
1334
1557
|
|
|
1335
|
-
|
|
1558
|
+
path: "field",
|
|
1336
1559
|
|
|
1337
|
-
|
|
1560
|
+
code: "ERROR_CODE",
|
|
1338
1561
|
|
|
1339
|
-
|
|
1562
|
+
message: "Validation error message"
|
|
1340
1563
|
|
|
1341
|
-
|
|
1564
|
+
}
|
|
1342
1565
|
|
|
1343
|
-
|
|
1566
|
+
]
|
|
1344
1567
|
|
|
1345
1568
|
}
|
|
1346
|
-
|
|
1347
1569
|
```
|
|
1348
1570
|
|
|
1349
1571
|
## Nested Objects and Array Field Paths
|
|
@@ -1372,14 +1594,13 @@ An error can be returned as:
|
|
|
1372
1594
|
|
|
1373
1595
|
{
|
|
1374
1596
|
|
|
1375
|
-
|
|
1597
|
+
path: "email",
|
|
1376
1598
|
|
|
1377
|
-
|
|
1599
|
+
code: "INVALID_EMAIL",
|
|
1378
1600
|
|
|
1379
|
-
|
|
1601
|
+
message: "Invalid email format for attribute email"
|
|
1380
1602
|
|
|
1381
1603
|
}
|
|
1382
|
-
|
|
1383
1604
|
```
|
|
1384
1605
|
|
|
1385
1606
|
### Nested Object
|
|
@@ -1436,16 +1657,15 @@ its complete nested path:
|
|
|
1436
1657
|
|
|
1437
1658
|
{
|
|
1438
1659
|
|
|
1439
|
-
|
|
1660
|
+
path: "address.location.latitude",
|
|
1440
1661
|
|
|
1441
|
-
|
|
1662
|
+
code: "INVALID_TYPE",
|
|
1442
1663
|
|
|
1443
|
-
|
|
1664
|
+
message:
|
|
1444
1665
|
|
|
1445
|
-
|
|
1666
|
+
"Invalid type for attribute address.location.latitude, required number value"
|
|
1446
1667
|
|
|
1447
1668
|
}
|
|
1448
|
-
|
|
1449
1669
|
```
|
|
1450
1670
|
|
|
1451
1671
|
Nested paths use dot notation:
|
|
@@ -1457,7 +1677,6 @@ address.city
|
|
|
1457
1677
|
address.location.latitude
|
|
1458
1678
|
|
|
1459
1679
|
address.location.longitude
|
|
1460
|
-
|
|
1461
1680
|
```
|
|
1462
1681
|
|
|
1463
1682
|
### Array Elements
|
|
@@ -1492,16 +1711,15 @@ The invalid third element is reported as:
|
|
|
1492
1711
|
|
|
1493
1712
|
{
|
|
1494
1713
|
|
|
1495
|
-
|
|
1714
|
+
path: "marks[2]",
|
|
1496
1715
|
|
|
1497
|
-
|
|
1716
|
+
code: "OUT_OF_RANGE",
|
|
1498
1717
|
|
|
1499
|
-
|
|
1718
|
+
message:
|
|
1500
1719
|
|
|
1501
|
-
|
|
1720
|
+
"Attribute marks[2] should have a value between 0 and 100"
|
|
1502
1721
|
|
|
1503
1722
|
}
|
|
1504
|
-
|
|
1505
1723
|
```
|
|
1506
1724
|
|
|
1507
1725
|
Array paths use zero-based indexes:
|
|
@@ -1513,7 +1731,6 @@ marks[0]
|
|
|
1513
1731
|
marks[1]
|
|
1514
1732
|
|
|
1515
1733
|
marks[2]
|
|
1516
|
-
|
|
1517
1734
|
```
|
|
1518
1735
|
|
|
1519
1736
|
### Nested Fields Inside Arrays
|
|
@@ -1529,7 +1746,6 @@ products[0].quantity
|
|
|
1529
1746
|
products[1].quantity
|
|
1530
1747
|
|
|
1531
1748
|
products[2].price
|
|
1532
|
-
|
|
1533
1749
|
```
|
|
1534
1750
|
|
|
1535
1751
|
This provides enough information for consumers to identify the exact
|
|
@@ -1574,391 +1790,387 @@ Result:
|
|
|
1574
1790
|
|
|
1575
1791
|
{
|
|
1576
1792
|
|
|
1577
|
-
|
|
1793
|
+
"email": "Invalid email format for attribute email",
|
|
1578
1794
|
|
|
1579
|
-
|
|
1795
|
+
"address.location.latitude": "Invalid type for attribute address.location.latitude, required number value",
|
|
1580
1796
|
|
|
1581
|
-
|
|
1797
|
+
"marks[2]": "Attribute marks[2] should have a value between 0 and 100"
|
|
1582
1798
|
|
|
1583
1799
|
}
|
|
1584
|
-
|
|
1585
1800
|
```
|
|
1586
1801
|
|
|
1587
|
-
## Examples
|
|
1802
|
+
## Examples and Usage
|
|
1588
1803
|
|
|
1589
1804
|
### Sample Validation Rule
|
|
1590
1805
|
|
|
1591
1806
|
sample-1
|
|
1592
1807
|
|
|
1593
|
-
```
|
|
1808
|
+
```js
|
|
1594
1809
|
|
|
1595
1810
|
{
|
|
1596
1811
|
|
|
1597
|
-
|
|
1812
|
+
firstName: {
|
|
1598
1813
|
|
|
1599
|
-
|
|
1814
|
+
mandatory: true,
|
|
1600
1815
|
|
|
1601
|
-
|
|
1816
|
+
allowNull: false,
|
|
1602
1817
|
|
|
1603
|
-
|
|
1818
|
+
type: "string",
|
|
1604
1819
|
|
|
1605
|
-
|
|
1820
|
+
minLength: 3,
|
|
1606
1821
|
|
|
1607
|
-
|
|
1822
|
+
minLengthError: "First name must have minimum 3 characters."
|
|
1608
1823
|
|
|
1609
|
-
|
|
1824
|
+
},
|
|
1610
1825
|
|
|
1611
|
-
|
|
1826
|
+
lastName: {
|
|
1612
1827
|
|
|
1613
|
-
|
|
1828
|
+
mandatory: false,
|
|
1614
1829
|
|
|
1615
|
-
|
|
1830
|
+
allowNull: true,
|
|
1616
1831
|
|
|
1617
|
-
|
|
1832
|
+
type: "string",
|
|
1618
1833
|
|
|
1619
|
-
|
|
1834
|
+
},
|
|
1620
1835
|
|
|
1621
|
-
|
|
1836
|
+
email: {
|
|
1622
1837
|
|
|
1623
|
-
|
|
1838
|
+
mandatory: true,
|
|
1624
1839
|
|
|
1625
|
-
|
|
1840
|
+
allowNull: false,
|
|
1626
1841
|
|
|
1627
|
-
|
|
1842
|
+
type: "email",
|
|
1628
1843
|
|
|
1629
|
-
|
|
1844
|
+
},
|
|
1630
1845
|
|
|
1631
|
-
|
|
1846
|
+
phone: {
|
|
1632
1847
|
|
|
1633
|
-
|
|
1848
|
+
mandatory: true,
|
|
1634
1849
|
|
|
1635
|
-
|
|
1850
|
+
allowNull: false,
|
|
1636
1851
|
|
|
1637
|
-
|
|
1852
|
+
type: "string",
|
|
1638
1853
|
|
|
1639
|
-
|
|
1854
|
+
},
|
|
1640
1855
|
|
|
1641
|
-
|
|
1856
|
+
age: {
|
|
1642
1857
|
|
|
1643
|
-
|
|
1858
|
+
mandatory: false,
|
|
1644
1859
|
|
|
1645
|
-
|
|
1860
|
+
type: "number",
|
|
1646
1861
|
|
|
1647
|
-
|
|
1862
|
+
min: 1,
|
|
1648
1863
|
|
|
1649
|
-
|
|
1864
|
+
max: 120,
|
|
1650
1865
|
|
|
1651
|
-
|
|
1866
|
+
},
|
|
1652
1867
|
|
|
1653
1868
|
};
|
|
1654
|
-
|
|
1655
1869
|
```
|
|
1656
1870
|
|
|
1657
1871
|
sample-2
|
|
1658
1872
|
|
|
1659
|
-
```
|
|
1873
|
+
```js
|
|
1660
1874
|
|
|
1661
1875
|
{
|
|
1662
1876
|
|
|
1663
|
-
|
|
1877
|
+
id: {
|
|
1664
1878
|
|
|
1665
|
-
|
|
1879
|
+
mandatory: true,
|
|
1666
1880
|
|
|
1667
|
-
|
|
1881
|
+
allowNull: true,
|
|
1668
1882
|
|
|
1669
|
-
|
|
1883
|
+
type: "uuidv4",
|
|
1670
1884
|
|
|
1671
|
-
|
|
1885
|
+
},
|
|
1672
1886
|
|
|
1673
|
-
|
|
1887
|
+
batchId: {
|
|
1674
1888
|
|
|
1675
|
-
|
|
1889
|
+
mandatory: true,
|
|
1676
1890
|
|
|
1677
|
-
|
|
1891
|
+
allowNull: true,
|
|
1678
1892
|
|
|
1679
|
-
|
|
1893
|
+
type: "objectId",
|
|
1680
1894
|
|
|
1681
|
-
|
|
1895
|
+
},
|
|
1682
1896
|
|
|
1683
|
-
|
|
1897
|
+
firstName: {
|
|
1684
1898
|
|
|
1685
|
-
|
|
1899
|
+
mandatory: true,
|
|
1686
1900
|
|
|
1687
|
-
|
|
1901
|
+
type: "string",
|
|
1688
1902
|
|
|
1689
|
-
|
|
1903
|
+
minLength: 3,
|
|
1690
1904
|
|
|
1691
|
-
|
|
1905
|
+
},
|
|
1692
1906
|
|
|
1693
|
-
|
|
1907
|
+
lastName: {
|
|
1694
1908
|
|
|
1695
|
-
|
|
1909
|
+
mandatory: false,
|
|
1696
1910
|
|
|
1697
|
-
|
|
1911
|
+
allowNull: true,
|
|
1698
1912
|
|
|
1699
|
-
|
|
1913
|
+
type: "string",
|
|
1700
1914
|
|
|
1701
|
-
|
|
1915
|
+
},
|
|
1702
1916
|
|
|
1703
|
-
|
|
1917
|
+
age: {
|
|
1704
1918
|
|
|
1705
|
-
|
|
1919
|
+
type: "number",
|
|
1706
1920
|
|
|
1707
|
-
|
|
1921
|
+
min: 0.1,
|
|
1708
1922
|
|
|
1709
|
-
|
|
1923
|
+
max: 120,
|
|
1710
1924
|
|
|
1711
|
-
|
|
1925
|
+
},
|
|
1712
1926
|
|
|
1713
|
-
|
|
1927
|
+
isAdult: {
|
|
1714
1928
|
|
|
1715
|
-
|
|
1929
|
+
type: "boolean",
|
|
1716
1930
|
|
|
1717
|
-
|
|
1931
|
+
},
|
|
1718
1932
|
|
|
1719
|
-
|
|
1933
|
+
totalWins: {
|
|
1720
1934
|
|
|
1721
|
-
|
|
1935
|
+
type: "number",
|
|
1722
1936
|
|
|
1723
|
-
|
|
1937
|
+
min: 0,
|
|
1724
1938
|
|
|
1725
|
-
|
|
1939
|
+
preventDecimal: true,
|
|
1726
1940
|
|
|
1727
|
-
|
|
1941
|
+
},
|
|
1728
1942
|
|
|
1729
|
-
|
|
1943
|
+
email: {
|
|
1730
1944
|
|
|
1731
|
-
|
|
1945
|
+
regex: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,}$/,
|
|
1732
1946
|
|
|
1733
|
-
|
|
1947
|
+
},
|
|
1734
1948
|
|
|
1735
|
-
|
|
1949
|
+
githubLink: {
|
|
1736
1950
|
|
|
1737
|
-
|
|
1951
|
+
type: "url",
|
|
1738
1952
|
|
|
1739
|
-
|
|
1953
|
+
},
|
|
1740
1954
|
|
|
1741
|
-
|
|
1955
|
+
accountStatus: {
|
|
1742
1956
|
|
|
1743
|
-
|
|
1957
|
+
type: "enum",
|
|
1744
1958
|
|
|
1745
|
-
|
|
1959
|
+
enumValues: ["Active", "Inactive", 200],
|
|
1746
1960
|
|
|
1747
|
-
|
|
1961
|
+
},
|
|
1748
1962
|
|
|
1749
|
-
|
|
1963
|
+
marks: {
|
|
1750
1964
|
|
|
1751
|
-
|
|
1965
|
+
range: "0-100",
|
|
1752
1966
|
|
|
1753
|
-
|
|
1967
|
+
},
|
|
1754
1968
|
|
|
1755
|
-
|
|
1969
|
+
allMarks: {
|
|
1756
1970
|
|
|
1757
|
-
|
|
1971
|
+
type: "array",
|
|
1758
1972
|
|
|
1759
|
-
|
|
1973
|
+
allowEmptyArray: false,
|
|
1760
1974
|
|
|
1761
|
-
|
|
1975
|
+
elementConstraints: {
|
|
1762
1976
|
|
|
1763
|
-
|
|
1977
|
+
type: "number",
|
|
1764
1978
|
|
|
1765
|
-
|
|
1979
|
+
allowNull: false,
|
|
1766
1980
|
|
|
1767
|
-
|
|
1981
|
+
range: "0-100",
|
|
1768
1982
|
|
|
1769
|
-
|
|
1983
|
+
},
|
|
1770
1984
|
|
|
1771
|
-
|
|
1985
|
+
},
|
|
1772
1986
|
|
|
1773
|
-
|
|
1987
|
+
totalScore: {
|
|
1774
1988
|
|
|
1775
|
-
|
|
1989
|
+
type: "number",
|
|
1776
1990
|
|
|
1777
|
-
|
|
1991
|
+
dependency: {
|
|
1778
1992
|
|
|
1779
|
-
|
|
1993
|
+
result: {
|
|
1780
1994
|
|
|
1781
|
-
|
|
1995
|
+
setDependencyRule: (totalScore, result) => {
|
|
1782
1996
|
|
|
1783
|
-
|
|
1997
|
+
return { mandatory: true, allowNull: false, type: "string" };
|
|
1784
1998
|
|
|
1785
|
-
|
|
1999
|
+
},
|
|
1786
2000
|
|
|
1787
|
-
|
|
2001
|
+
},
|
|
1788
2002
|
|
|
1789
|
-
|
|
2003
|
+
},
|
|
1790
2004
|
|
|
1791
|
-
|
|
2005
|
+
},
|
|
1792
2006
|
|
|
1793
|
-
|
|
2007
|
+
result: {
|
|
1794
2008
|
|
|
1795
|
-
|
|
2009
|
+
type: "string",
|
|
1796
2010
|
|
|
1797
|
-
|
|
2011
|
+
dependency: {
|
|
1798
2012
|
|
|
1799
|
-
|
|
2013
|
+
totalScore: {
|
|
1800
2014
|
|
|
1801
|
-
|
|
2015
|
+
setDependencyRule: (result, totalScore) => {
|
|
1802
2016
|
|
|
1803
|
-
|
|
2017
|
+
return { mandatory: true, allowNull: false, type: "number" };
|
|
1804
2018
|
|
|
1805
|
-
|
|
2019
|
+
},
|
|
1806
2020
|
|
|
1807
|
-
|
|
2021
|
+
},
|
|
1808
2022
|
|
|
1809
|
-
|
|
2023
|
+
},
|
|
1810
2024
|
|
|
1811
|
-
|
|
2025
|
+
},
|
|
1812
2026
|
|
|
1813
|
-
|
|
2027
|
+
minSalary: {
|
|
1814
2028
|
|
|
1815
|
-
|
|
2029
|
+
mandatory: true,
|
|
1816
2030
|
|
|
1817
|
-
|
|
2031
|
+
min: 1,
|
|
1818
2032
|
|
|
1819
|
-
|
|
2033
|
+
type: "number",
|
|
1820
2034
|
|
|
1821
|
-
|
|
2035
|
+
dependency: {
|
|
1822
2036
|
|
|
1823
|
-
|
|
2037
|
+
maxSalary: {
|
|
1824
2038
|
|
|
1825
|
-
|
|
2039
|
+
setDependencyRule: (minSalary, maxSalary) => {
|
|
1826
2040
|
|
|
1827
|
-
|
|
2041
|
+
return {
|
|
1828
2042
|
|
|
1829
|
-
|
|
2043
|
+
mandatory: true,
|
|
1830
2044
|
|
|
1831
|
-
|
|
2045
|
+
min: minSalary + 1,
|
|
1832
2046
|
|
|
1833
|
-
|
|
2047
|
+
minError: "maxSalary must be more than minSalary",
|
|
1834
2048
|
|
|
1835
|
-
|
|
2049
|
+
};
|
|
1836
2050
|
|
|
1837
|
-
|
|
2051
|
+
},
|
|
1838
2052
|
|
|
1839
|
-
|
|
2053
|
+
},
|
|
1840
2054
|
|
|
1841
|
-
|
|
2055
|
+
},
|
|
1842
2056
|
|
|
1843
|
-
|
|
2057
|
+
},
|
|
1844
2058
|
|
|
1845
|
-
|
|
2059
|
+
maxSalary: {
|
|
1846
2060
|
|
|
1847
|
-
|
|
2061
|
+
dependency: {
|
|
1848
2062
|
|
|
1849
|
-
|
|
2063
|
+
minSalary: {
|
|
1850
2064
|
|
|
1851
|
-
|
|
2065
|
+
setDependencyRule: (maxSalary, minSalary) => {
|
|
1852
2066
|
|
|
1853
|
-
|
|
2067
|
+
return {
|
|
1854
2068
|
|
|
1855
|
-
|
|
2069
|
+
mandatory: true,
|
|
1856
2070
|
|
|
1857
|
-
|
|
2071
|
+
max: maxSalary - 1,
|
|
1858
2072
|
|
|
1859
|
-
|
|
2073
|
+
maxError: "minSalary must be less than maxSalary",
|
|
1860
2074
|
|
|
1861
|
-
|
|
2075
|
+
};
|
|
1862
2076
|
|
|
1863
|
-
|
|
2077
|
+
},
|
|
1864
2078
|
|
|
1865
|
-
|
|
2079
|
+
},
|
|
1866
2080
|
|
|
1867
|
-
|
|
2081
|
+
},
|
|
1868
2082
|
|
|
1869
|
-
|
|
2083
|
+
},
|
|
1870
2084
|
|
|
1871
|
-
|
|
2085
|
+
address: {
|
|
1872
2086
|
|
|
1873
|
-
|
|
2087
|
+
mandatory: true,
|
|
1874
2088
|
|
|
1875
|
-
|
|
2089
|
+
type: "object",
|
|
1876
2090
|
|
|
1877
|
-
|
|
2091
|
+
allowEmptyObject: false,
|
|
1878
2092
|
|
|
1879
|
-
|
|
2093
|
+
objectAttr: {
|
|
1880
2094
|
|
|
1881
|
-
|
|
2095
|
+
country: { mandatory: true, type: "string" },
|
|
1882
2096
|
|
|
1883
|
-
|
|
2097
|
+
state: {
|
|
1884
2098
|
|
|
1885
|
-
|
|
2099
|
+
mandatory: true,
|
|
1886
2100
|
|
|
1887
|
-
|
|
2101
|
+
type: "string",
|
|
1888
2102
|
|
|
1889
|
-
|
|
2103
|
+
},
|
|
1890
2104
|
|
|
1891
|
-
|
|
2105
|
+
city: {},
|
|
1892
2106
|
|
|
1893
|
-
|
|
2107
|
+
zip: {
|
|
1894
2108
|
|
|
1895
|
-
|
|
2109
|
+
mandatory: true,
|
|
1896
2110
|
|
|
1897
|
-
|
|
2111
|
+
type: "string",
|
|
1898
2112
|
|
|
1899
|
-
|
|
2113
|
+
},
|
|
1900
2114
|
|
|
1901
|
-
|
|
2115
|
+
position: {
|
|
1902
2116
|
|
|
1903
|
-
|
|
2117
|
+
mandatory: true,
|
|
1904
2118
|
|
|
1905
|
-
|
|
2119
|
+
type: "object",
|
|
1906
2120
|
|
|
1907
|
-
|
|
2121
|
+
allowEmptyObject: false,
|
|
1908
2122
|
|
|
1909
|
-
|
|
2123
|
+
objectAttr: {
|
|
1910
2124
|
|
|
1911
|
-
|
|
2125
|
+
lattitude: { mandatory: true, type: "number" },
|
|
1912
2126
|
|
|
1913
|
-
|
|
2127
|
+
longitude: {
|
|
1914
2128
|
|
|
1915
|
-
|
|
2129
|
+
mandatory: true,
|
|
1916
2130
|
|
|
1917
|
-
|
|
2131
|
+
type: "number",
|
|
1918
2132
|
|
|
1919
|
-
|
|
2133
|
+
},
|
|
1920
2134
|
|
|
1921
|
-
|
|
2135
|
+
},
|
|
1922
2136
|
|
|
1923
|
-
|
|
2137
|
+
},
|
|
1924
2138
|
|
|
1925
|
-
|
|
2139
|
+
},
|
|
1926
2140
|
|
|
1927
|
-
|
|
2141
|
+
},
|
|
1928
2142
|
|
|
1929
2143
|
}
|
|
1930
|
-
|
|
1931
2144
|
```
|
|
1932
2145
|
|
|
1933
2146
|
### Usage
|
|
1934
2147
|
|
|
1935
|
-
####
|
|
2148
|
+
#### Creating a route with payload validation middleware
|
|
1936
2149
|
|
|
1937
|
-
```
|
|
2150
|
+
```js
|
|
1938
2151
|
|
|
1939
|
-
|
|
2152
|
+
**// validatePayload is the middleware that invokes perfectPayload()**
|
|
1940
2153
|
|
|
1941
2154
|
router.post(
|
|
1942
2155
|
|
|
1943
|
-
|
|
2156
|
+
"/payload-validation",
|
|
1944
2157
|
|
|
1945
|
-
|
|
2158
|
+
validatePayload({ rule: <your validation rule json object> }),
|
|
1946
2159
|
|
|
1947
|
-
|
|
2160
|
+
(req, res) => res.send("OK")
|
|
1948
2161
|
|
|
1949
2162
|
);
|
|
1950
|
-
|
|
1951
2163
|
```
|
|
1952
2164
|
|
|
1953
|
-
####
|
|
2165
|
+
#### ES Modules middleware example
|
|
1954
2166
|
|
|
1955
|
-
```
|
|
1956
|
-
import {
|
|
2167
|
+
```js
|
|
2168
|
+
import { perfectPayload } from "perfect-payload";
|
|
1957
2169
|
|
|
1958
2170
|
export const validatePayload = ({ rule }) => {
|
|
1959
2171
|
return (req, res, next) => {
|
|
1960
2172
|
try {
|
|
1961
|
-
const { statusCode, ...response } =
|
|
2173
|
+
const { statusCode, ...response } = perfectPayload(req?.body, rule);
|
|
1962
2174
|
|
|
1963
2175
|
if (+statusCode >= 200 && +statusCode <= 299) {
|
|
1964
2176
|
req.validatedBody = response?.validatedPayload;
|
|
@@ -1974,15 +2186,15 @@ export const validatePayload = ({ rule }) => {
|
|
|
1974
2186
|
};
|
|
1975
2187
|
```
|
|
1976
2188
|
|
|
1977
|
-
####
|
|
2189
|
+
#### CommonJS middleware example
|
|
1978
2190
|
|
|
1979
|
-
```
|
|
2191
|
+
```js
|
|
1980
2192
|
function validatePayload({ rule }) {
|
|
1981
2193
|
return async (req, res, next) => {
|
|
1982
2194
|
try {
|
|
1983
|
-
const {
|
|
2195
|
+
const { perfectPayload } = await import("perfect-payload");
|
|
1984
2196
|
|
|
1985
|
-
const { statusCode, ...response } =
|
|
2197
|
+
const { statusCode, ...response } = perfectPayload(req?.body, rule);
|
|
1986
2198
|
|
|
1987
2199
|
if (+statusCode >= 200 && +statusCode <= 299) {
|
|
1988
2200
|
req.validatedBody = response?.validatedPayload;
|
|
@@ -2002,7 +2214,7 @@ function validatePayload({ rule }) {
|
|
|
2002
2214
|
module.exports = { validatePayload };
|
|
2003
2215
|
```
|
|
2004
2216
|
|
|
2005
|
-
|
|
2217
|
+
---
|
|
2006
2218
|
|
|
2007
2219
|
This documentation provides a comprehensive guide to using the data
|
|
2008
2220
|
|