When you're dealing with complex and structured data, you need to determine whether the data is valid or not. JSON-Schema is the standard of JSON documents that describes the structure and the requirements of your JSON data. In this two-part series, you'll learn how to use JSON-Schema to validate data.
Let's say you have a database of users where each record looks similar to this example:
{ "id": 64209690, "name": "Jane Smith", "email": "jane.smith@gmail.com", "phone": "07777 888 999", "address": { "street": "Flat 1, 188 High Street Kensington", "postcode": "W8 5AA", "city": "London", "country": "United Kingdom" }, "personal": { "DOB": "1982-08-16", "age": 33, "gender": "female" }, "connections": [ { "id": "35434004285760", "name": "John Doe", "connType": "friend", "since": "2014-03-25" }, { "id": 13418315, "name": "James Smith", "connType": "relative", "relation": "husband", "since": "2012-07-03" } ], "feeds": { "news": true, "sport": true, "fashion": false }, "createdAt": "2022-08-31T18:13:48.616Z" }
The question we are going to deal with is how to determine whether the record like the one above is valid or not.
Examples are very useful but not sufficient when describing your data requirements. JSON-Schema comes to the rescue. This is one of the possible schemas describing a user record:
{ "$schema": "https://json-schema.org/draft-04/schema#", "id": "https://mynet.com/schemas/user.json#", "title": "User", "description": "User profile with connections", "type": "object", "properties": { "id": { "description": "positive integer or string of digits", "type": ["string", "integer"], "pattern": "^[1-9][0-9]*$", "minimum": 1 }, "name": { "type": "string", "maxLength": 128 }, "email": { "type": "string", "format": "email" }, "phone": { "type": "string", "pattern": "^[0-9()\-\.\s]+$" }, "address": { "type": "object", "additionalProperties": { "type": "string" }, "maxProperties": 6, "required": ["street", "postcode", "city", "country"] }, "personal": { "type": "object", "properties": { "DOB": { "type": "string", "format": "date" }, "age": { "type": "integer", "minimum": 13 }, "gender": { "enum": ["female", "male"] } } "required": ["DOB", "age"], "additionalProperties": false }, "connections": { "type": "array", "maxItems": 150, "items": { "title": "Connection", "description": "User connection schema", "type": "object", "properties": { "id": { "type": ["string", "integer"], "pattern": "^[1-9][0-9]*$", "minimum": 1 }, "name": { "type": "string", "maxLength": 128 }, "since": { "type": "string", "format": "date" }, "connType": { "type": "string" }, "relation": {}, "close": {} }, "oneOf": [ { "properties": { "connType": { "enum": ["relative"] }, "relation": { "type": "string" } }, "dependencies": { "relation": ["close"] } }, { "properties": { "connType": { "enum": ["friend", "colleague", "other"] }, "relation": { "not": {} }, "close": { "not": {} } } } ], "required": ["id", "name", "since", "connType"], "additionalProperties": false } }, "feeds": { "title": "feeds", "description": "Feeds user subscribes to", "type": "object", "patternProperties": { "^[A-Za-z]+$": { "type": "boolean" } }, "additionalProperties": false }, "createdAt": { "type": "string", "format": "date-time" } } }
Have a look at the schema above and the user record it describes (that is valid according to this schema). There is a lot of explaining to do here.
JavaScript code to validate the user record against the schema could be:
const Ajv = require('ajv'); const ajv = Ajv({allErrors: true}); const valid = ajv.validate(userSchema, userData); if (valid) { console.log('User data is valid'); } else { console.log('User data is INVALID!'); console.log(ajv.errors); }
or for better performance:
const validate = ajv.compile(userSchema); const valid = validate(userData); if (!valid) console.log(validate.errors);
If you are using ESM, do this:
import ajv from "ajv" const ajv = Ajv({allErrors: true, code: {esm: true}}); const valid = ajv.validate(userSchema, userData); if (valid) { console.log('User data is valid'); } else { console.log('User data is INVALID!'); console.log(ajv.errors); }
ESM (ECMAScript modules) is a module format that is slowly replacing CommonJS (the format that uses require()
due to it working in web browsers as well as Node.js and because of new features it offers.
If you want the most modern code, try using ESM. Otherwise, if you want to stick with the current most common format, just use CJS. In this tutorial, the examples will use CJS, but I recommend trying out ESM as a replacement.
For more information on ESM and how to use it in Node.js, check out this article on ESM.
All the code samples are available in the GitHub repo tutsplus-json-schema. You can also try it in the browser.
Ajv, the validator used in the example, is the fastest JSON-Schema validator for JavaScript. I created it, so I am going to use it in this tutorial.
Before we continue, let's quickly deal with all the whys.
This tutorial includes several relatively simple tasks to help you better understand the JSON schema and how it can be used. There are simple JavaScript scripts to check that you've done them correctly. To run them you will need to install node.js (you need no experience with it). Just install nvm (node version manager) and a recent node.js version:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash nvm install node
You also need to clone the repo and run npm install
(it will install Ajv validator).
JSON-schema is always an object. Its properties are called "keywords". Some of them describe the rules for the data (e.g., "type" and "properties"), and some describe the schema itself ("$schema", "id", "title", "description")—we will get to them later.
The data is valid according to the schema if it is valid according to all keywords in this schema—that's really simple.
Because most JSON data consists of objects with multiple properties, the keyword "properties" is probably the most commonly used keyword. It only applies to objects (see the next section about what "apply" means).
You might have noticed in the example above that each property inside the "properties" keyword describes the corresponding property in your data.
The value of each property is itself a JSON-schema—JSON-schema is a recursive standard. Each property in the data should be valid according to the corresponding schema in the "properties" keyword.
The important thing here is that the "properties" keyword doesn't make any property required; it only defines schemas for the properties that are present in the data.
For example, if our schema is:
{ "properties": { "foo": { "type": "string" } } }
then objects with or without property "foo" can be valid according to this schema:
{foo: "bar"}, {foo: "bar", baz: 1}, {baz: 1}, {} // all valid
and only objects that have property foo that is not a string are invalid:
{ foo: 1 } // invalid
Try this example in the browser.
You've already figured out what the keyword "type" does. It is probably the most important keyword. Its value (a string or array of strings) defines what type (or types) the data must be to be valid.
As you can see in the example above, the user data must be an object.
Most keywords apply to certain data types—for example, the keyword "properties" only applies to objects, and the keyword "pattern" only applies to strings.
What does "apply" mean? Let's say we have a really simple schema:
{ "pattern": "^[0-9]+$" }
You may expect that to be valid according to such schema, the data must be a string matching the pattern:
"12345"
But the JSON-schema standard specifies that if a keyword doesn't apply to the data type, then the data is valid according to this keyword. That means that any data that is not of type "string" is valid according to the schema above—numbers, arrays, objects, boolean, and even null. If you want only strings matching the pattern to be valid, your schema should be:
{ "type": "string", "pattern": "^[0-9]+$" }
Because of this, you can make very flexible schemas that will validate multiple data types.
Look at the property "id" in the user example. It should be valid according to this schema:
{ "type": ["string", "integer"], "pattern": "^[1-9][0-9]*$", "minimum": 1 }
This schema requires that the data to be valid should be either a "string" or an "integer". There is also the keyword "pattern" that applies only to strings; it requires that the string should consist of digits only and not start from 0. There is the keyword "minimum" that applies only to numbers; it requires that the number should be not less than 1.
Another, more verbose, way to express the same requirement is:
{ "anyOf": [ { "type": "string", "pattern": "^[1-9][0-9]*$" }, { "type": "integer", "minimum": 1 }, ] }
But because of the way JSON-schema is defined, this schema is equivalent to the first one, which is shorter and faster to validate in most validators.
Data types you can use in schemas are "object", "array", "number", "integer", "string", "boolean", and "null". Note that "number" includes "integer"—all integers are numbers too.
There are several keywords to validate numbers. All the keywords in this section apply to numbers only (including integers).
"minimum" and "maximum" are self-explanatory. In addition to them, there are the keywords "exclusiveMinimum" and "exclusiveMaximum". In our user example, the user age is required to be an integer that is 13 or bigger. If the schema for the user age were:
{ "type": "integer", "minimum": 13, "exclusiveMinimum": true }
then this schema would have required that user age is strictly bigger than 13, i.e. the lowest allowed age would be 14.
Another keyword to validate numbers is "multipleOf". Its name also explains what it does, and you can check out the JSON-schema keywords reference to see how it works.
There are also several keywords to validate strings. All the keywords in this section apply to strings only.
"maxLength" and "minLength" require that the string is not longer or not shorter than the given number. The JSON-schema standard requires that a unicode pair, e.g. emoji character, is counted as a single character. JavaScript counts it as two characters when you access the .length
property of strings.
Some validators determine string lengths as required by the standard, and some do it the JavaScript way, which is faster. Ajv allows you to specify how to determine string lengths, and the default is to comply with the standard.
var schema = { "maxLength": 2 }; var ajv = Ajv(); // count unicode pairs as one character ajv.validate(schema, "????????"); // true ajv.validate(schema, "????????!"); // false var ajv = Ajv({unicode: false}); // count unicode pairs as two characters ajv.validate(schema, "????"); // true ajv.validate(schema, "????!"); // false
You have already seen the "pattern" keyword in action—it simply requires that the data string matches the regular expression defined according to the same standard that is used in JavaScript. See the example below for schemas and matching regular expressions:
{"pattern": "^[1-9][0-9]*$" }; /^[1-9][0-9]*$/; // id {"pattern": "^[A-Za-z]+$" }; /^[a-z]+$/i; // letters {"pattern": "^[0-9()\-\.\s]+$"}; /^[0-9()\-\.\s]+$/; // phone
The "format" keyword defines the semantic validation of strings, such as "email", "date" or "date-time" in the user example. JSON-Schema also defines the formats "uri", "hostname", "ipv4", and "ipv6". Validators define formats differently, optimizing for validation speed or for correctness. Ajv gives you a choice:
var ajv = Ajv(); // "fast" format validation ajv.validate({"format": "date"}, "2015-12-24"); // true ajv.validate({"format": "date"}, "2015-14-33"); // true var ajv = Ajv({format: 'full'); // more thorough format validation ajv.validate({"format": "date"}, "2015-12-24"); // true ajv.validate({"format": "date"}, "2015-14-33"); // false
Most validators allow you to define custom formats either as regular expressions or validating functions. We could define a custom format "phone" for our schema to use it in multiple places:
ajv.addFormat('phone', /^[0-9()\-\.\s]+$/);
and then the schema for the phone
property would be:
{ "type": "string", "format": "phone" }
Create a schema that will require the data to be a date (string) or a year (number) and that a year is bigger than or equal to 1976.
Put your answer in the file part1/task1/date_schema.json
and run node part1/task1/validate
to check it.
In addition to "properties", you can see several other keywords in our user example that apply to objects.
The "required" keyword lists properties that must be present in the object for it to be valid. As you remember, the "properties" keyword doesn't require properties, it only validates them if they are present. "required" complements "properties", allowing you to define which properties are required and which are optional.
If we had this schema:
{ "properties": { "foo": { "type": "string" } }, "required": ["foo"] }
then all objects without property foo
would be invalid.
Please note that this schema still doesn't require our data to be an object—all other data types are valid according to it. To require that our data is an object, we have to add the "type" keyword to it.
Try the example above in the browser.
The "patternProperties" keyword allows you to define schemas according to which the data property value should be valid if the property name matches the regular expression. It can be combined with the "properties" keyword in the same schema.
The feeds property in the user example should be valid according to this schema:
{ "type": "object", "patternProperties": { "^[A-Za-z]+$": { "type": "boolean" } }, "additionalProperties": false }
To be valid, feeds
should be an object with properties whose names consist only of Latin letters and whose values are boolean.
The "additionalProperties" keyword allows you to either define the schema according to which all other keywords (not used in "properties" and not matching "patternProperties") should be valid, or to prohibit other properties completely, as we did in the feeds
property schema above.
In the following example, "additionalProperties" is used to create a simple schema for hash of integers in a certain range:
var schema = { "type": "object", "additionalProperties" { "type": "integer", "minimum": 0, "maximum": 65535 } }; var validate = ajv.compile(schema); validate({a:1,b:10,c:100}); // true validate({d:1,e:10,f:100000}); // false validate({g:1,h:10,i:10.5}); // false validate({j:1,k:10,l:'abc'}); // false
The "maxProperties" and "minProperties" keywords allow you to limit the number of properties in the object. In our user example, the schema for the address
property is:
{ "type": "object", "additionalProperties": { "type": "string" }, "maxProperties": 6, "required": ["street", "postcode", "city", "country"] }
This schema requires that the address is an object with required properties street
, postcode
, city
and country
, allows two additional properties ("maxProperties" is 6), and requires that all properties are strings.
"dependencies" is probably the most complex and confusing and the most rarely used keyword, but is a very powerful keyword at the same time. It allows you to define the requirements that the data should satisfy if it has certain properties.
There are two types of such requirements to the object: to have some other properties (it is called "property dependency") or to satisfy some schema ("schema dependency").
In our user example, one of the possible schemas that the user connection should be valid against is this:
{ "properties": { "connType": { "enum": ["relative"] }, "relation": { "type": "string" } }, "dependencies": { "relation": ["close"] } }
It requires that the connType
property is equal to "relative" (see the "enum" keyword below) and that if the relation
property is present, it is a string.
It does not require that relation
is present, but the "dependencies" keyword requires that IF the relation
property is present, THEN the close
property should be present too.
There are no validation rules defined for the close
property in our schema, although from the example we can see that it probably must be boolean. One of the ways we could correct this omission is to change the "dependencies" keyword to use "schema dependency":
"dependencies": { "relation": { "properties": { "close": { "type": "boolean" }, }, "required": ["close"] } }
You can play with the updated user example in the browser.
Please note that the schema in the "relation" property in the "dependencies" keyword is used to validate the parent object (i.e. connection) and not the value of the relation
property in the data.
Your database contains humans and machines. Using only the keywords that I've explained so far create a schema to validate both of them. A sample human object:
{ "human": true, "name": "Jane", "gender": "female", "DOB": "1985-08-12" }
A sample machine object:
{ "model": "TX1000", "made": "2013-08-29" }
Note that it should be one schema to validate both humans and machines, not two schemas.
Put your answer in the file part1/task2/human_machine_schema.json
and run node part1/task2/validate
to check it.
Hints: use the "dependencies" keyword, and look in the file part1/task2/invalid.json
to see which objects should be invalid.
Which objects that probably should be invalid too are not in the invalid.json
file?
The main takeaway from this task is the fact that the purpose of validation is not only to validate all valid objects as valid. I've heard this argument many times: "This schema validates my valid data as valid, therefore it is correct." This argument is wrong because you don't need to do much to achieve it—an empty schema will do the job, because it validates any data as valid.
I think that the main purpose of validation is to validate invalid data as invalid, and that's where all the complexity comes from.
There are several keywords to validate arrays (and they apply to arrays only).
"maxItems" and "minItems" require that the array has not more (or not less) than a certain number of items. In the user example, the schema requires that the number of connections is not more than 150.
The "items" keyword defines a schema (or schemas) according to which the items should be valid. If the value of this keyword is an object (as in the user example), then this object is a schema according to which the data should be valid.
If the value of the "items" keyword is an array, then this array contains schemas according to which the corresponding items should be valid:
{ "type": "array", "items": [ { "type": "integer" }, { "type": "string" } ] }
The schema in the simple example above requires that the data is an array, with the first item that is an integer and the second that is a string.
What about items after these two? The schema above defines no requirements for other items. They can be defined with the "additionalItems" keyword.
The "additionalItems" keyword only applies to the situation in which the "items" keyword is an array and there are more items in the data than in the "items" keyword. In all other cases (no "items" keyword, it is an object, or there are not more items in the data), the "additionalItems" keyword will be ignored, regardless of its value.
If the "additionalItems" keyword is true, it is simply ignored. If it is false and the data has more items than the "items" keyword—then validation fails:
const schema = { "type": "array", "items": [ { "type": "integer" }, { "type": "string" } ], "additionalItems": false }; const validate = ajv.compile(schema); console.log(validate([1, "foo", 3])); // false
If the "additionalItems" keyword is an object, then this object is a schema according to which all additional items should be valid:
const schema = { "type": "array", "items": [ { "type": "integer" }, { "type": "string" } ], "additionalItems": { "type": "integer" } }; const validate = ajv.compile(schema); console.log(validate([1, "foo", 3])); // true console.log(validate([1, "foo", 3, 4])); // true console.log(validate([1, "foo", "bar"])); // false
Please experiment with these examples to see how "items" and "additionalItems" work.
The last keyword that applies to arrays is "uniqueItems". If its value is true
, it simply requires that all items in the array are different.
Validating the keyword "uniqueItems" can be computationally expensive, so some validators chose not to implement it or to do so only partially.
Ajv has an option to ignore this keyword:
const schema = { "type": "array", "uniqueItems": true }; const ajv = Ajv(); // validate uniqueItems ajv.validate(schema, [1, 2, 3]); // true ajv.validate(schema, [1, 2, 2]); // false const ajv = Ajv({uniqueItems: false}); // ignore uniqueItems ajv.validate(schema, [1, 2, 3]); // true ajv.validate(schema, [1, 2, 2]); // true
One of the ways to create a date object in JavaScript is to pass from 2 to 7 numbers to the Date constructor:
const date = new Date(2015, 2, 15); // Sun Mar 15 2015 00:00:00 GMT+0000, month is 0 based const date2 = new Date(year, month0, day, hour, minute, seconds, ms);
You have an array. Create a schema that will validate that this is a valid list of arguments for the Date constructor.
Put your answer in the file part1/task3/date_args_schema.json
and run node part1/task3/validate
to check it.
The "enum" keyword requires that the data is equal to one of several values. It applies to all types of data.
In the user example, it is used to define the gender
property inside the personal
property as either "male" or "female". It is also used to define the connType
property in user connections.
The "enum" keyword can be used with any types of values, not only strings and numbers, although it is not very common.
It can also be used to require that data is equal to a specific value, as in the user example:
"properties": { "connType": { "enum": ["relative"] }, ... }
Another keyword supported in the latest versions of JSON Schema is constant:
const schema = { "constant": "relative" }; const ajv = Ajv({v5: true}); // this options enables v5 keywords const validate = ajv.compile(schema); validate("relative"); // true validate("other"); // false
There are several keywords that allow you to define an advanced logic involving validation against multiple schemas. All the keywords in this section apply to all data types.
Our user example uses the "oneOf" keyword to define requirements to the user connection. This keyword is valid if the data successfully validates against exactly one schema inside the array.
If data is invalid according to all schemas in the "oneOf" keyword or valid according to two or more schemas, then the data is invalid.
Let's look more closely at our example:
{ ... "oneOf": [ { "properties": { "connType": { "enum": ["relative"] }, "relation": { "type": "string" } }, "dependencies": { "relation": ["close"] } }, { "properties": { "connType": { "enum": ["friend", "colleague", "other"] }, "relation": { "not": {} }, "close": { "not": {} } } } ], ... }
The schema above requires that user connection is either "relative" (connType property), in which case it may have properties relation
(string) and close
(boolean), or one of types "friend", "colleague" or "other" (in which case it must not have properties relation
and close
).
These schemas for user connection are mutually exclusive, because there is no data that can satisfy both of them. So if the connection is valid and has type "relative", there is no point validating it against the second schema—it will always be invalid. Nevertheless, any validator will always be validating data against both schemas to make sure that it is only valid according to one.
There is another keyword that allows you to avoid it: "anyOf". This keyword simply requires that data is valid according to some schema in the array (possibly to several schemas).
In cases such as above, where schemas are mutually exclusive and no data can be valid according to more than one schema, it is better to use the "anyOf" keyword—it will validate faster in most cases (apart from the one, in which the data is valid according to the last schema).
Using "oneOf" in cases where "anyOf" does an equally good job is a very common mistake that negatively affects validation performance.
Our user example would also benefit from replacing "oneOf" with "anyOf".
There are some cases, though, when we really need the "oneOf" keyword:
{ "type": "string", "oneOf": [ { "pattern": "apple" } { "pattern": "orange" } ] }
The schema above will successfully validate strings that mention oranges or apples, but not both (and there do exist strings that can mention both). If that's what you need, then you need to use "oneOf".
Comparing with boolean operators, "anyOf" is like boolean OR and "oneOf" is like XOR (exclusive OR). The fact that JavaScript (and many other languages) don't define operators for exclusive OR shows that it is rarely needed.
There is also the keyword "allOf". It requires that the data is valid according to all schemas in the array:
{ "allOf": [ { "$ref": "http://mynet.com/schemas/feed.json#" }, { "maxProperties": 5 } ] }
The "$ref" keyword allows you to require that data is valid according to the schema in another file (or some part of it). We will be looking at it in the second part of this tutorial.
Another mistake is to put more than absolutely necessary inside schemas in the "oneOf", "anyOf" and "allOf" keyword. For example, in our user example, we could put inside "anyOf" all requirements that the connection should satisfy.
We also could have unnecessarily complicated the example with apple and oranges:
{ "oneOf": [ { "type": "string", "pattern": "apple" } { "type": "string", "pattern": "orange" } ] }
Another "logical" keyword is "not". It requires that the data is NOT valid according to the schema that is the value of this keyword.
For example:
{ "type": "string", "not": { "pattern": "apple" } }
The schema above would require that the data is a string that does not contain "apple".
In the user example, the "not" keyword is used to prevent some properties from being used in one of the cases in "oneOf", although they are defined:
{ "properties": { "connType": { "enum": ["friend", "colleague", "other"] }, "relation": { "not": {} }, "close": { "not": {} } } }
The value of the "not" keyword in the example above is an empty schema. An empty schema will validate any value as valid, and the "not" keyword will make it invalid. So the schema validation will fail if the object has the property relation
or close
. You can achieve the same with the combination of "not" and "required" keywords.
Another use of the "not" keyword is to define the schema that requires that an array contains an item that is valid according to some schema:
{ "not": { "items": { "not: { "type": "integer", "minimum": 5 } } } }
The schema above requires that the data is an array and it contains at least one integer item greater than or equal to 5.
V5 proposals include the keyword "contains" to satisfy this requirement.
var schema = { "type": "array", "contains": { "type": "integer", "minimum": 5 } }; const ajv = Ajv({v5: true}); // enables v5 keywords const validate = ajv.compile(schema); validate([3, 4, 5]); // true validate([1, 2, 3]); // false
You have a database of users that all match schema from the user example. Create a schema according to which only users that satisfy all these criteria will be valid:
Put your answer in the file part1/task4/filter_schema.json
and run node part1/task4/validate
to check it.
The test data is simplified, so please do not use the "required" keyword in your schema.
Some keywords used in the user example do not directly affect validation, but they describe the schema itself.
The "$schema" keyword defines the URI of the meta-schema for the schema. The schema itself is a JSON document, and it can be validated using JSON-schema. A JSON-schema that defines any JSON-schema is called a meta-schema. The URI for the meta-schema for draft 2020-12 of the JSON-schema standard is https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-01.
If you extend the standard, it is recommended that you use a different value of the "$schema" property.
"id" is the schema URI. It can be used to refer to the schema (or some part of it) from another schema using the "$ref" keyword—see the second part of the tutorial. Most validators, including Ajv, allow any string as "id". According to the standard, the schema id should be a valid URI that can be used to download the schema.
You can also use "title" and "description" to describe the schema. They are not used during the validation. Both these keywords can be used on any level inside the schema to describe some parts of it, as is done in the user example.
Create an example of a user record that when validated with the example user schema will have 8 or more errors.
Put your answer in the file part1/task5/invalid_user.json
and run node part1/task5/validate
to check it.
What is still very wrong with our user schema?
You might have noticed that JSON Schema is not the only schema format that Ajv supports. Ajv also supports JSON Type Definition, an alternative to JSON Schema that is simpler and more concise. For example, if we were trying to describe a patch statement that complies with JSON Patch using JSON Typedef, the schema would look something like this:
{ "definitions": { "addop": { "properties": { "op": { "type": "string", "enum": [ "add", "replace", "test" ] }, "value": {}, "path": { "type": "string" } } }, "removeop": { "properties": { "op": { "type": "string", "enum": [ "remove" ] }, "path": { "type": "string" } } }, "moveop": { "properties": { "op": { "type": "string", "enum": [ "move", "copy" ] }, "path": { "type": "string" }, "from": { "type": "string" } } } }, "elements": { "discriminator": "op", "mapping": { "add": { "ref": "addop" }, "replace": { "ref": "addop" }, "test": { "ref": "addop" }, "move": { "ref": "moveop" }, "copy": { "ref": "moveop" } } } }
Then, to validate data with the schema using Ajv, I would do this:
"use strict"; if (process.env.NODE_ENV != "test") return; const Ajv = require("ajv/dist/jtd"); const assert = require("assert"); const patchData = require("./data"); const patchSchema = require("./schema"); const ajv = Ajv({ allErrors: true }); const validate = ajv.compile(patchSchema); assert(test(validate)); console.log("Patch schema OK"); function test(validate) { const valid = validate(userData); if (valid) { console.log("Patch data is valid!"); } else { console.log("Patch data is INVALID!"); console.log(validate.errors); } return valid; }
The code is almost identical to using JSON Schema, except that you have to import ajv/dist/jtd
.
By now you know all the validation keywords defined by the standard, and you should be able to create quite complex schemas. As your schemas grow, you will be reusing some parts of them. Schemas can be structured into multiple parts and even multiple files to avoid repetition. We will be doing this in the second part of the tutorial.
We also will:
Thanks for reading!
This post has been updated with contributions from Jacob Jackson. Jacob is a web developer, technical writer, a freelancer, and an open-source contributor.
The Best Small Business Web Designs by DesignRush
/Create Modern Vue Apps Using Create-Vue and Vite
/Pros and Cons of Using WordPress
/How to Fix the “There Has Been a Critical Error in Your Website” Error in WordPress
How To Fix The “There Has Been A Critical Error in Your Website” Error in WordPress
/How Long Does It Take to Learn JavaScript?
/The Best Way to Deep Copy an Object in JavaScript
/Adding and Removing Elements From Arrays in JavaScript
/Create a JavaScript AJAX Post Request: With and Without jQuery
/5 Real-Life Uses for the JavaScript reduce() Method
/How to Enable or Disable a Button With JavaScript: jQuery vs. Vanilla
/How to Enable or Disable a Button With JavaScript: jQuery vs Vanilla
/Confirm Yes or No With JavaScript
/How to Change the URL in JavaScript: Redirecting
/15+ Best WordPress Twitter Widgets
/27 Best Tab and Accordion Widget Plugins for WordPress (Free & Premium)
/21 Best Tab and Accordion Widget Plugins for WordPress (Free & Premium)
/30 HTML Best Practices for Beginners
/31 Best WordPress Calendar Plugins and Widgets (With 5 Free Plugins)
/25 Ridiculously Impressive HTML5 Canvas Experiments
/How to Implement Email Verification for New Members
/How to Create a Simple Web-Based Chat Application
/30 Popular WordPress User Interface Elements
/Top 18 Best Practices for Writing Super Readable Code
/Best Affiliate WooCommerce Plugins Compared
/18 Best WordPress Star Rating Plugins
/10+ Best WordPress Twitter Widgets
/20+ Best WordPress Booking and Reservation Plugins
/Working With Tables in React: Part Two
/Best CSS Animations and Effects on CodeCanyon
/30 CSS Best Practices for Beginners
/How to Create a Custom WordPress Plugin From Scratch
/10 Best Responsive HTML5 Sliders for Images and Text… and 3 Free Options
/16 Best Tab and Accordion Widget Plugins for WordPress
/18 Best WordPress Membership Plugins and 5 Free Plugins
/25 Best WooCommerce Plugins for Products, Pricing, Payments and More
/10 Best WordPress Twitter Widgets
1 /12 Best Contact Form PHP Scripts for 2020
/20 Popular WordPress User Interface Elements
/10 Best WordPress Star Rating Plugins
/12 Best CSS Animations on CodeCanyon
/12 Best WordPress Booking and Reservation Plugins
/12 Elegant CSS Pricing Tables for Your Latest Web Project
/24 Best WordPress Form Plugins for 2020
/14 Best PHP Event Calendar and Booking Scripts
/Create a Blog for Each Category or Department in Your WooCommerce Store
/8 Best WordPress Booking and Reservation Plugins
/Best Exit Popups for WordPress Compared
/Best Exit Popups for WordPress Compared
/11 Best Tab & Accordion WordPress Widgets & Plugins
/12 Best Tab & Accordion WordPress Widgets & Plugins
1New Course: Practical React Fundamentals
/Preview Our New Course on Angular Material
/Build Your Own CAPTCHA and Contact Form in PHP
/Object-Oriented PHP With Classes and Objects
/Best Practices for ARIA Implementation
/Accessible Apps: Barriers to Access and Getting Started With Accessibility
/Dramatically Speed Up Your React Front-End App Using Lazy Loading
/15 Best Modern JavaScript Admin Templates for React, Angular, and Vue.js
/15 Best Modern JavaScript Admin Templates for React, Angular and Vue.js
/19 Best JavaScript Admin Templates for React, Angular, and Vue.js
/New Course: Build an App With JavaScript and the MEAN Stack
/Hands-on With ARIA: Accessibility Recipes for Web Apps
/10 Best WordPress Facebook Widgets
13 /Hands-on With ARIA: Accessibility for eCommerce
/New eBooks Available for Subscribers
/Hands-on With ARIA: Homepage Elements and Standard Navigation
/Site Accessibility: Getting Started With ARIA
/How Secure Are Your JavaScript Open-Source Dependencies?
/New Course: Secure Your WordPress Site With SSL
/Testing Components in React Using Jest and Enzyme
/Testing Components in React Using Jest: The Basics
/15 Best PHP Event Calendar and Booking Scripts
/Create Interactive Gradient Animations Using Granim.js
/How to Build Complex, Large-Scale Vue.js Apps With Vuex
1 /Examples of Dependency Injection in PHP With Symfony Components
/Set Up Routing in PHP Applications Using the Symfony Routing Component
1 /A Beginner’s Guide to Regular Expressions in JavaScript
/Introduction to Popmotion: Custom Animation Scrubber
/Introduction to Popmotion: Pointers and Physics
/New Course: Connect to a Database With Laravel’s Eloquent ORM
/How to Create a Custom Settings Panel in WooCommerce
/Building the DOM faster: speculative parsing, async, defer and preload
1 /20 Useful PHP Scripts Available on CodeCanyon
3 /How to Find and Fix Poor Page Load Times With Raygun
/Introduction to the Stimulus Framework
/Single-Page React Applications With the React-Router and React-Transition-Group Modules
12 Best Contact Form PHP Scripts
1 /Getting Started With the Mojs Animation Library: The ShapeSwirl and Stagger Modules
/Getting Started With the Mojs Animation Library: The Shape Module
/Getting Started With the Mojs Animation Library: The HTML Module
/Project Management Considerations for Your WordPress Project
/8 Things That Make Jest the Best React Testing Framework
/Creating an Image Editor Using CamanJS: Layers, Blend Modes, and Events
/New Short Course: Code a Front-End App With GraphQL and React
/Creating an Image Editor Using CamanJS: Applying Basic Filters
/Creating an Image Editor Using CamanJS: Creating Custom Filters and Blend Modes
/Modern Web Scraping With BeautifulSoup and Selenium
/Challenge: Create a To-Do List in React
1Deploy PHP Web Applications Using Laravel Forge
/Getting Started With the Mojs Animation Library: The Burst Module
/10 Things Men Can Do to Support Women in Tech
/A Gentle Introduction to Higher-Order Components in React: Best Practices
/Challenge: Build a React Component
/A Gentle Introduction to HOC in React: Learn by Example
/A Gentle Introduction to Higher-Order Components in React
/Creating Pretty Popup Messages Using SweetAlert2
/Creating Stylish and Responsive Progress Bars Using ProgressBar.js
/18 Best Contact Form PHP Scripts for 2022
/How to Make a Real-Time Sports Application Using Node.js
/Creating a Blogging App Using Angular & MongoDB: Delete Post
/Set Up an OAuth2 Server Using Passport in Laravel
/Creating a Blogging App Using Angular & MongoDB: Edit Post
/Creating a Blogging App Using Angular & MongoDB: Add Post
/Introduction to Mocking in Python
/Creating a Blogging App Using Angular & MongoDB: Show Post
/Creating a Blogging App Using Angular & MongoDB: Home
/Creating a Blogging App Using Angular & MongoDB: Login
/Creating Your First Angular App: Implement Routing
/Persisted WordPress Admin Notices: Part 4
/Creating Your First Angular App: Components, Part 2
/Persisted WordPress Admin Notices: Part 3
/Creating Your First Angular App: Components, Part 1
/How Laravel Broadcasting Works
/Persisted WordPress Admin Notices: Part 2
/Create Your First Angular App: Storing and Accessing Data
/Persisted WordPress Admin Notices: Part 1
/Error and Performance Monitoring for Web & Mobile Apps Using Raygun
/Using Luxon for Date and Time in JavaScript
7 /How to Create an Audio Oscillator With the Web Audio API
/How to Cache Using Redis in Django Applications
/20 Essential WordPress Utilities to Manage Your Site
/Introduction to API Calls With React and Axios
/Beginner’s Guide to Angular 4: HTTP
/Rapid Web Deployment for Laravel With GitHub, Linode, and RunCloud.io
/Beginners Guide to Angular 4: Routing
/Beginner’s Guide to Angular 4: Services
/Beginner’s Guide to Angular 4: Components
/Creating a Drop-Down Menu for Mobile Pages
/Introduction to Forms in Angular 4: Writing Custom Form Validators
/10 Best WordPress Booking & Reservation Plugins
/Getting Started With Redux: Connecting Redux With React
/Getting Started With Redux: Learn by Example
/Getting Started With Redux: Why Redux?
/How to Auto Update WordPress Salts
/How to Download Files in Python
/Eloquent Mutators and Accessors in Laravel
1 /10 Best HTML5 Sliders for Images and Text
/Site Authentication in Node.js: User Signup
/Creating a Task Manager App Using Ionic: Part 2
/Creating a Task Manager App Using Ionic: Part 1
/Introduction to Forms in Angular 4: Reactive Forms
/Introduction to Forms in Angular 4: Template-Driven Forms
/24 Essential WordPress Utilities to Manage Your Site
/25 Essential WordPress Utilities to Manage Your Site
/Get Rid of Bugs Quickly Using BugReplay
1 /Manipulating HTML5 Canvas Using Konva: Part 1, Getting Started
/10 Must-See Easy Digital Downloads Extensions for Your WordPress Site
/22 Best WordPress Booking and Reservation Plugins
/Understanding ExpressJS Routing
/15 Best WordPress Star Rating Plugins
/Creating Your First Angular App: Basics
/Inheritance and Extending Objects With JavaScript
/Introduction to the CSS Grid Layout With Examples
1Performant Animations Using KUTE.js: Part 5, Easing Functions and Attributes
Performant Animations Using KUTE.js: Part 4, Animating Text
/Performant Animations Using KUTE.js: Part 3, Animating SVG
/New Course: Code a Quiz App With Vue.js
/Performant Animations Using KUTE.js: Part 2, Animating CSS Properties
Performant Animations Using KUTE.js: Part 1, Getting Started
/10 Best Responsive HTML5 Sliders for Images and Text (Plus 3 Free Options)
/Single-Page Applications With ngRoute and ngAnimate in AngularJS
/Deferring Tasks in Laravel Using Queues
/Site Authentication in Node.js: User Signup and Login
/Working With Tables in React, Part Two
/Working With Tables in React, Part One
/How to Set Up a Scalable, E-Commerce-Ready WordPress Site Using ClusterCS
/New Course on WordPress Conditional Tags
/TypeScript for Beginners, Part 5: Generics
/Building With Vue.js 2 and Firebase
6 /Best Unique Bootstrap JavaScript Plugins
/Essential JavaScript Libraries and Frameworks You Should Know About
/Vue.js Crash Course: Create a Simple Blog Using Vue.js
/Build a React App With a Laravel RESTful Back End: Part 1, Laravel 5.5 API
/API Authentication With Node.js
/Beginner’s Guide to Angular: HTTP
/Beginner’s Guide to Angular: Routing
/Beginners Guide to Angular: Routing
/Beginner’s Guide to Angular: Services
/Beginner’s Guide to Angular: Components
/How to Create a Custom Authentication Guard in Laravel
/Learn Computer Science With JavaScript: Part 3, Loops
/Build Web Applications Using Node.js
/Learn Computer Science With JavaScript: Part 4, Functions
/Learn Computer Science With JavaScript: Part 2, Conditionals
/Create Interactive Charts Using Plotly.js, Part 5: Pie and Gauge Charts
/Create Interactive Charts Using Plotly.js, Part 4: Bubble and Dot Charts
Create Interactive Charts Using Plotly.js, Part 3: Bar Charts
/Awesome JavaScript Libraries and Frameworks You Should Know About
/Create Interactive Charts Using Plotly.js, Part 2: Line Charts
/Bulk Import a CSV File Into MongoDB Using Mongoose With Node.js
/Build a To-Do API With Node, Express, and MongoDB
/Getting Started With End-to-End Testing in Angular Using Protractor
/TypeScript for Beginners, Part 4: Classes
/Object-Oriented Programming With JavaScript
/10 Best Affiliate WooCommerce Plugins Compared
/Stateful vs. Stateless Functional Components in React
/Make Your JavaScript Code Robust With Flow
/Build a To-Do API With Node and Restify
/Testing Components in Angular Using Jasmine: Part 2, Services
/Testing Components in Angular Using Jasmine: Part 1
/Creating a Blogging App Using React, Part 6: Tags
/React Crash Course for Beginners, Part 3
/React Crash Course for Beginners, Part 2
/React Crash Course for Beginners, Part 1
/Set Up a React Environment, Part 4
1 /Set Up a React Environment, Part 3
/New Course: Get Started With Phoenix
/Set Up a React Environment, Part 2
/Set Up a React Environment, Part 1
/Command Line Basics and Useful Tricks With the Terminal
/How to Create a Real-Time Feed Using Phoenix and React
/Build a React App With a Laravel Back End: Part 2, React
/Build a React App With a Laravel RESTful Back End: Part 1, Laravel 9 API
/Creating a Blogging App Using React, Part 5: Profile Page
/Pagination in CodeIgniter: The Complete Guide
/JavaScript-Based Animations Using Anime.js, Part 4: Callbacks, Easings, and SVG
/JavaScript-Based Animations Using Anime.js, Part 3: Values, Timeline, and Playback
/Learn to Code With JavaScript: Part 1, The Basics
/10 Elegant CSS Pricing Tables for Your Latest Web Project
/Getting Started With the Flux Architecture in React
/Getting Started With Matter.js: The Composites and Composite Modules
Getting Started With Matter.js: The Engine and World Modules
/10 More Popular HTML5 Projects for You to Use and Study
/Understand the Basics of Laravel Middleware
/Iterating Fast With Django & Heroku
/Creating a Blogging App Using React, Part 4: Update & Delete Posts
/Creating a jQuery Plugin for Long Shadow Design
/How to Register & Use Laravel Service Providers
2 /Unit Testing in React: Shallow vs. Static Testing
/Creating a Blogging App Using React, Part 3: Add & Display Post
/Creating a Blogging App Using React, Part 2: User Sign-Up
20 /Creating a Blogging App Using React, Part 1: User Sign-In
/Creating a Grocery List Manager Using Angular, Part 2: Managing Items
/9 Elegant CSS Pricing Tables for Your Latest Web Project
/Dynamic Page Templates in WordPress, Part 3
/Angular vs. React: 7 Key Features Compared
/Creating a Grocery List Manager Using Angular, Part 1: Add & Display Items
New eBooks Available for Subscribers in June 2017
/Create Interactive Charts Using Plotly.js, Part 1: Getting Started
/The 5 Best IDEs for WordPress Development (And Why)
/33 Popular WordPress User Interface Elements
/New Course: How to Hack Your Own App
/How to Install Yii on Windows or a Mac
/What Is a JavaScript Operator?
/How to Register and Use Laravel Service Providers
/
waly Good blog post. I absolutely love this…