1
1
# Tiny Schema Validator
2
2
3
- JSON schema validator with great type inference.
3
+ JSON schema validator with excellent type inference for JavaScript and TypeScript .
4
4
5
5
[ ![ GitHub license] ( https://img.shields.io/github/license/5alidz/tiny-schema-validator )] ( https://github.com/5alidz/tiny-schema-validator/blob/master/LICENSE ) ![ Minzipped size] ( https://img.shields.io/bundlephobia/minzip/tiny-schema-validator.svg )
6
6
@@ -19,47 +19,72 @@ yarn add tiny-schema-validator
19
19
``` js
20
20
import { createSchema , _ } from ' tiny-schema-validator' ;
21
21
22
- export const Person = createSchema ({
23
- name : _ .string ({
24
- maxLength : [ 100 , ' too-long ' ] ,
25
- minLength : [ 2 , ' too-short ' ] ,
22
+ export const User = createSchema ({
23
+ metadata : _ .record ({
24
+ date_created : _ . number () ,
25
+ id : _ . string () ,
26
26
}),
27
- age: _ .number ({
28
- max: [150 , ' too-old' ],
29
- min: [13 , ' too-young' ],
27
+ profile: _ .record ({
28
+ name: _ .string ({
29
+ maxLength: [100 , ' too-long' ],
30
+ minLength: [2 , ' too-short' ],
31
+ }),
32
+ age: _ .number ({
33
+ max: [150 , ' too-old' ],
34
+ min: [13 , ' too-young' ],
35
+ }),
36
+ email: _ .string ({
37
+ pattern: [/ ^ [^ @] + @[^ @] + \. [^ @] + $ / , ' invalid-email' ],
38
+ }),
30
39
}),
31
- email: _ .string ({
32
- pattern: [/ ^ [^ @] + @[^ @] + \. [^ @] + $ / , ' invalid-email' ],
33
- });
40
+ payment_status: _ .union (
41
+ _ .constant (' pending' ),
42
+ _ .constant (' failed' ),
43
+ _ .constant (' success' ),
44
+ _ .constant (' canceled' )
45
+ ),
34
46
});
35
47
```
36
48
37
49
and in TypeScript, everything is the same, but to get the data type inferred from the schema, you can do this:
38
50
39
51
``` ts
40
52
/*
41
- PersonType {
42
- name: string;
43
- age: number;
44
- email: string;
53
+ UserType {
54
+ metadata: {
55
+ date_created: number;
56
+ id: string;
57
+ };
58
+ profile: {
59
+ name: string;
60
+ age: number;
61
+ email: string;
62
+ };
63
+ payment_status: 'pending' | 'failed' | 'success' | 'canceled';
45
64
}
46
65
*/
47
- export type PersonType = ReturnType <typeof Person .produce >;
66
+ export type UserType = ReturnType <typeof User .produce >;
48
67
```
49
68
50
- ## Schema
69
+ ### Using the schema
51
70
52
71
When you create a schema, you will get a nice API to handle multiple use-cases in the client and the server.
53
72
54
73
- ` is(data: any): boolean ` check if the data is valid (eager evaluation)
55
74
- ` validate(data: any): Errors ` errors returned has the same shape as the schema you defined (does not throw)
56
75
- ` produce(data: any): data ` throws an error when the data is invalid. otherwise, it returns data
57
76
- ` embed(config?: { optional: boolean }) ` embeds the schema in other schemas
58
- - ` traverse(visitor, data?, eager?) ` (advanced) see usage below.
77
+ - ` source ` the schema itself in a parsable format
59
78
60
- Continuing from the previous example:
79
+ example usage :
61
80
62
81
``` js
82
+ const Person = createSchema ({
83
+ name: _ .string (),
84
+ age: _ .number (),
85
+ email: _ .string (),
86
+ });
87
+
63
88
const john = { name: ' john' , age: 42 , email: ' john@gmail.com' };
64
89
Person .is ({}); // false
65
90
Person .is (john); // true
@@ -84,18 +109,37 @@ const GroupOfPeople = createSchema({
84
109
85
110
## Validators
86
111
112
+ All validators are required by default.
87
113
All validators are accessible with the ` _ ` (underscore) namespace; The reason for using ` _ ` instead of a good name like ` validators ` is developer experience, and you can alias it to whatever you want.
88
114
89
115
``` js
90
116
import { _ as validators } from ' tiny-schema-validator' ;
91
117
92
- validators .string (); // creates a string validator
118
+ // NOTE: when you call a validator you just create an object with { type: '<type of validator>', ...options }
119
+ // this is just a shorthand for that.
120
+
121
+ // example of all validators and corresponding Typescript types
122
+ validators .string (); // string
123
+ validators .number (); // number
124
+ validators .boolean (); // boolean
125
+ validators .constant (42 ); // 42
126
+ validators .union (validators .constant (1 ), validators .constant (2 ), validators .constant (3 )); // 1 | 2 | 3
127
+ validators .list ([validators .number (), validators .number ()]); // [number, number]
128
+ validators .listof (validators .string ()); // string[]
129
+ validators .recordof (validators .string ()); // Record<string, string>
130
+ validators .record ({
131
+ timestamp: validators .number (),
132
+ id: validators .string (),
133
+ }); // { timestamp: number; id: string; }
93
134
```
94
135
95
136
Check out the full validators API below:
96
137
97
138
| validator | signature | props |
98
139
| :-------- | ------------------------------- | :------------------------------------------------------------- |
140
+ | | | |
141
+ | constant | ` constant(value) ` | value: ` string \| number \| boolean ` |
142
+ | | | |
99
143
| string | ` string(options?) ` | options (optional): Object |
100
144
| | | - ` optional : boolean ` defaults to false |
101
145
| | | - ` maxLength: [length: number, error: string] ` |
@@ -111,6 +155,8 @@ Check out the full validators API below:
111
155
| boolean | ` boolean(options?) ` | options(optional): Object |
112
156
| | | - ` optional: boolean ` default to false |
113
157
| | | |
158
+ | union | ` union(...validators) ` | validators: Array of validators as paramaters |
159
+ | | | |
114
160
| list | ` list(validators[], options?) ` | validators: Array of validators |
115
161
| | | options(optional): Object |
116
162
| | | - ` optional: boolean ` default to false |
@@ -154,91 +200,6 @@ const Person = createSchema({
154
200
});
155
201
```
156
202
157
- ## Advanced usage
158
-
159
- In addition to validating data, you can also reuse your schema in other areas, like creating forms UI.
160
- ` traverse ` function come-in handy to help you achieve that.
161
-
162
- ### Example
163
-
164
- In this example, we will transform a schema to create meta-data to create form UI elements.
165
-
166
- ``` js
167
- const User = createSchema ({
168
- id: _ .string (),
169
- created: _ .number (),
170
- updated: _ .number (),
171
- profile: _ .record ({ username: _ .string (), email: _ .string (), age: _ .number () }),
172
- });
173
-
174
- const form_ui = User .traverse ({
175
- number ({ path, key }) {
176
- if (path .includes (' profile' )) return { type: ' number' , label: key };
177
- return null ; // otherwise ignore
178
- },
179
- string ({ path, key }) {
180
- if (path .includes (' profile' )) return { type: ' text' , label: key };
181
- return null ; // otherwise ignore
182
- },
183
- // this is required to get the type of "profile" correct
184
- record : () => null ,
185
- });
186
-
187
- console .log (form_ui); /*
188
- {
189
- profile: {
190
- username: { type: 'text', label: 'username' },
191
- email: { type: 'text', label: 'email' },
192
- age: { type: 'number', label: 'age' }
193
- }
194
- }
195
- */
196
- ```
197
-
198
- ### How to traverse
199
-
200
- there are a few considerations when defining ` visitor ` object:
201
-
202
- - In ` string | number | boolean ` visitors, returning ` null ` signals to ignore this node.
203
- - In ` record | recordof | list | listof ` visitors, returning ` null ` signals to visit its children.
204
- - To skip over ` record | recordof | list | listof ` nodes, return ` {} ` (empty object).
205
-
206
- Continuing from the previous ` User ` Example
207
-
208
- ``` js
209
- /*
210
- Say We need this structure:
211
- {
212
- profile: {
213
- type: 'container',
214
- children: [
215
- { type: 'text', label: 'username' },
216
- { type: 'text', label: 'email' },
217
- { type: 'number', label: 'age' }
218
- ]
219
- }
220
- }
221
- */
222
- const customTraverse = (key , validator ) => {
223
- const type = validator .type ;
224
-
225
- if (type == ' string' ) return { type: key == ' email' ? key : ' text' , label: key };
226
- if (type == ' number' ) return { type: ' number' , label: key };
227
- if (type == ' record' )
228
- return {
229
- type: ' container' ,
230
- children: Object .entries (validator .shape ).map (entry => customTraverse (... entry)),
231
- };
232
- return null ;
233
- };
234
-
235
- const form_ui = User .traverse ({
236
- record ({ path, key, validator }) {
237
- return customTraverse (key, validator);
238
- },
239
- });
240
- ```
241
-
242
203
## Caveats
243
204
244
205
- When using the ` recordof | listof | list ` validators, the optional property of the validator is ignored, example:
0 commit comments