Overwhelmed by the options for schema validation? Comparing tools can be confusing. With so many options, how do you know which one is best for your needs? In this post, we’ll compare AJV, Joi, Yup, and Zod in their functionality and usability.
What are Schemas?
A schema is a structure that defines the shape of your data. It shows how a model exists and how it relates to other models around it. A database schema is the skeleton structure that represents the logical view of the entire database. The schema doesn’t hold data itself, but its structure is imposed on the store data.
Ensuring the data follows the schema format is important, so validating your data against a schema is also important. There are libraries in Node.js that help make the process easier. This post will help you choose the right schema validation library for your project.
Schema Validator Option: AJV
AJV is one of the more popular validators available. It has a very rich API that is currently being used by a large number of applications in all environments. What makes AJV different from the rest and unique is AJV is a JSON Schema validator.
Let’s take a quick look at the syntax. Validating data involves writing out the expected schema, compiling it with AJV, and then validating it against any data.
The major issue with AJV is writing custom error messages, it requires a longer process than other schema validation libraries. Custom messages are very important. There is a workaround using libraries like AJV-errors, but it is more complicated than other libraries.
-
AJV uses JSON Schema to validate its data, which can be done across various languages.
-
Large community around it with ~12k stars on Github and 85M weekly npm downloads.
-
AJV currently is the fastest json schema validator library according to json-schema-benchmarks.
AJV is easy to use. All you have to do is write your schema, compile it, and validate if it works
const schema = {
type: "object",
properties: {
name: { type: "string" },
email: { type: "string"},
age: { type: "integer" },
account: {
type: "object",
properties: {
bank: {type: "string"},
account: {type: "integer"}
},
required: ["account","bank"]
},
},
required: ["name", "email", "account"],
additionalProperties: false,
}
The schema above has been written to represent the structure of the data. It contains name, email, age, and account properties fully represented in the schema and the expected property type. The additional properties field ensures the data fails when fields not present in the schema are present in the data. The required field is also necessary. It is an array of all fields that should be required in the object.
You may have noticed in the last snippet the other big complaint people have with JSON Schema: it is very verbose. Thankfully there is a tool that lets us write the same schema with a cleaner syntax: TypeBox. Here’s the above schema rewritten using TypeBox:
import { Static, Type } from '@sinclair/typebox'
const schema = Type.Object({
name: Type.String(),
email: Type.String(),
age: Type.Optional(Type.Number()),
account: Type.Object({
bank: Type.String(),
account: Type.Number()
})
}, { additionalProperties: false })
type Schema = Static<typeof schema>
Using TypeBox, the same schema requires half as many lines of code, making it as friendly to write as other schema validation options. It also generates static types, making it an excellent choice for TypeScript projects. TypeBox is becoming a popular option for JSON Schema usage. For example, it was recently integrated into the latest release of FeathersJS.
The resulting schema output is the same whether you use the more verbose JSON Schema syntax or choose to use TypeBox. Now let’s look at how to use the schema with AJV.
How to Validate a Schema in AJV
Validating the schema in AJV is as simple as compiling the schema and validating it against any data that should match the schema. You can see the process in the code sample below:
const validate = AJV.compile(schema);
const isValid = validate(data);
console.log(validate.errors) // NULL IF THERE ARE NO ERRORS
Schema Validator Option: Joi
Joi is a schema validation tool that runs in your browser and allows you to check that your data conforms to any schema format (including those defined with JSON-LD). Joi also provides tools for creating, comparing, and sharing schemas. The validator supports schema validation, definitions, introspections, and minimization.
As a bonus: Joi also has rich API and is in use by a large number of applications.
-
Largest community and support around it with ~19k stars on Github.
-
The bundle size is around 149kb minified.
-
It is built more for the server side and is recommended when working server side.
-
Joi has a very large API which means it’s well-documented, shortening the learning curve.
How to Validate a Schema in Joi
The code below describes writing a schema in Joi. the syntax is very easy to learn, and a large API to accommodate most validation use cases is present in Joi. Including a custom error message is also very easy as you can see from below.
const schema = Joi.object({
name: Joi.string().required('Name is required'),
email: Joi.string().email('An email should be sent').required(),
age: Joi.number().optional(),
account: Joi.object({
bank: Joi.string().required(),
account: Joi.string().required(),
})
});
You can validate your data against the schema using the code below
const { error, value } = schema.validate(data);
if(error) {
console.log(error.message)
}
console.log(value);
Schema Validator Option: Yup
Yup is a schema validator heavily inspired by Joi. The schema looks very similar to Joi and will be fairly simple to pick up if you already know Joi. Yup is built with client-side validations as its primary use case.
Yup is different from other mentioned validators because it separates the parsing of data and validating of this data into different steps. You can cast your data into a structure that you want it to be.
-
Schema is similar to Joi, and it also has similar methods.
-
It is primarily used on the client side and supports client-side Validations.
-
Bundle size of ~60kb minified. This is lower than AJV and Joi but larger than Zod.
-
Yup supports static type inference, but it isn’t necessarily aligned with TypeScript.
const yup = require('yup');
const { data } = require('./data');
const schema = yup.object({
name: yup.string().required(),
email: yup.string().email().required(),
age: yup.number().positive().optional(),
account: yup.object({
bank: yup.string().required(),
account: yup.string().required(),
})
});
The above schema looks very similar to Joi. An object is being validated, the name field ensures it is a string, and it is required. Yup also supports extra validations like the validation for email, as seen in the email field. Ensure to also check their documentation to see what is supported.
Incoming data is validated against the schema above. In Yup, you can either validate your data or cast it into the required feature. It returns a promise when you cast or validate your data, ensuring to handle the response with this in mind.
How to Validate a Schema in Yup
To validate a schema in Yup, you first cast the data:
schema.cast(data).then(data => console.log(data))
Then, assuming the data is in the expected format, Yup will validate the data:
schema.validate(data)
.then(value => console.log(value))
.catch(error => console.log(error))
Schema Validator Option: Zod
Zod is a schema validation tool that allows you to check your JSON Schema against actual data. It's fast, it's easy to use, and it has no dependencies. Zod is based on the JSON Schema Validator (Joi) but with a different architecture. Zod can be used as a standalone package or with the Joi library.
-
Zod supports static type inference, it works well with TypeScript.
-
Bundle size is around ~45kb.
How to Validate a Schema in Zod
Validating in Zod involves creating a schema to parse the data against the schema. The process is similar to the rest, except you are not validating but parsing. This means it throws an error if it fails.
const zod = require("zod")
const { data } = require("./data")
const schema = zod.object({
name: zod.string(),
email: zod.string().email(),
age: zod.number().positive().optional(),
account: zod.object({
bank: zod.string(),
account: zod.string(),
})
}).strict();
try{
schema.parse(data);
}
catch (e){
console.log(e);
}
Ensure to try-catch when you parse to handle the error as needed.
import * as zod from "zod";
const schema = zod.string();
type B = zod.infer<typeof schema>;
const y: B = 738.2; //IT THROWS AN ERROR
Conclusion
All schema validation libraries will work well enough to be used for your project. When looking on the server side, AJV and Joi are considered top choices in this case, while Yup and Zod are considered more on the client side.
Joi is my personal choice when strictly working with JavaScript on the server side because it has support for a lot more API, and it’s quicker to understand the schema. Joi also has great documentation to back up its largely available API.
When working with multiple languages or a microservice architecture using different stacks, AJV is a better choice mainly because you can share your schema across services, as the JSON-Schema can also be compiled by other languages. For TypeScript projects, TypeBox makes the syntax much cleaner.
On the client side, if you are working with TypeScript, Zod is a really good choice based on compatibility and may be the better choice for your project. However, if you have worked with Joi, the similarity of Yup with Joi makes the transition easier. They are both capable of the same things, and it’s totally up to you to figure out
Ready to enter the debate?
Join the Bitovi Discord server! Our community is available to help work through whatever you’re facing.