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/.babelrc +3 -3
- package/.github/workflows/publish.yml +124 -0
- package/LICENSE +21 -21
- package/README.md +1863 -375
- package/index.js +1429 -567
- package/package.json +36 -36
package/README.md
CHANGED
|
@@ -1,375 +1,1863 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
-
|
|
128
|
-
-
|
|
129
|
-
-
|
|
130
|
-
|
|
131
|
-
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
mandatory: true,
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
},
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
},
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
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.
|