JSON 07 ๐ Validating Objects and Required Fields
The required keyword is the assertion that certain properties must be present in an object. It is the simplest of the object-validation keywords, and it is the one that the developers most often misunderstand. The keyword takes an array of strings, and the array must contain unique values. An object instance is valid against the keyword if every string in the array is the name of a property in the instance, regardless of the property’s value . The keyword does not depend on the properties keyword: the properties describes the value of a property when it is present, and the required mandates that the property is present at all . The separation is the mechanism that lets the schema require a field without constraining its value, and it is the reason the required and the properties are configured independently.
The presence check is not the same as the value check. A property that is present with the value null satisfies the required array, because the keyword only checks the presence. The value check is the properties subschema’s job, and the type: "null" or the anyOf with the null branch is the mechanism that allows the null value explicitly . The distinction is the mechanism that separates “the field is present” from “the field has a valid value,” and the two checks are configured in the two different keywords.
Key point: The required keyword takes an array of unique strings, and an object is valid if every string is a property name in the instance. The keyword checks presence, not value. The dependentRequired keyword is the conditional version: it requires a property only when another property is present. The if/then/else keywords are the more general conditional mechanism for the value-based conditions.
Why the required keyword is the presence assertion
The presence problem. A JSON object can omit a property entirely, and the omission is different from the null value. The required keyword is the mechanism that distinguishes the two: a missing property fails the validation, and a null property passes the presence check if the properties schema allows the null type . The distinction is the reason the required and the type are separate keywords.
The independence problem. The required keyword does not depend on the properties keyword. A schema can have the required: ["name"] without the properties.name definition, and the validation still passes when the name is present with any value . The independence is the mechanism that lets the schema require a field without describing its value, and the properties is the optional refinement.
The conditional problem. A form may require a field only when another field has a specific valueโthe membershipNumber is required when the isMember is true. The dependentRequired keyword is the presence-based conditional: it requires a list of properties when a specific property is present . The if/then/else keywords are the value-based conditional: they apply a subschema when the instance validates against the if schema . The two mechanisms are the conditional validation, and the choice is the condition type.
The trade-off. The required keyword is the strict presence assertion, and the dependentRequired is the conditional one. The if/then/else is the most general, and it is the most complex. The three are the layers of the conditional validation, and the choice is the complexity against the precision.
a. The required keyword and the presence rules
The required keyword takes an array of strings, and the strings must be unique . An object is valid if every string is a property name in the instance. The property’s value is not checked by this keyword.
// The required keyword
{
"type": "object",
"properties": {
"name": { "type": "string" },
"email": { "type": "string", "format": "email" }
},
"required": ["name", "email"]
}
The {"name": "John", "email": "john@example.com"} is valid. The {"name": "John"} is invalid because the email is missing. The {"email": "john@example.com"} is invalid because the name is missing. The {} is invalid because both are missing . The {"name": "John", "email": null} is valid against the required, because the email is present, and the properties.email schema is the one that decides whether the null is allowed.
The required keyword is an assertion that applies only to the object instances. A non-object instance is valid against the keyword, because the keyword does not apply to it. The type: "object" is the mechanism that restricts the instance to the objects, and the two keywords together are the complete constraint .
// The non-object instance is valid against required
{
"required": ["name"]
}
// The "Hello World" string is valid.
// The {} object is invalid.
// The type: "object" is the restriction.
The additionalProperties keyword is the complement. It controls the unspecified properties, and the false value is the strict mode that rejects the extra properties. The combination of the required, the properties, and the additionalProperties is the complete object schema .
// The strict object schema
{
"type": "object",
"properties": {
"name": { "type": "string" }
},
"required": ["name"],
"additionalProperties": false
}
b. The conditional presence with dependentRequired
The dependentRequired keyword is the presence-based conditional. It takes an object whose keys are the property names and whose values are the arrays of the properties that are required when the key is present .
// The dependentRequired keyword
{
"type": "object",
"properties": {
"creditCardNumber": { "type": "string" },
"address": { "type": "string" }
},
"dependentRequired": {
"creditCardNumber": ["address"]
}
}
The {"creditCardNumber": "1234", "address": "123 Main St"} is valid. The {"creditCardNumber": "1234"} is invalid because the address is required when the creditCardNumber is present. The {"address": "123 Main St"} is valid because the creditCardNumber is absent, and the dependency does not trigger . The {} is valid because neither property is present.
The dependentRequired is the presence-only dependency. It does not check the value of the creditCardNumber; it only checks whether the property is present. The if/then/else is the value-based conditional, and it is the mechanism for the rules that depend on the value, not the presence.
// The if-then-else conditional
{
"type": "object",
"properties": {
"isMember": { "type": "boolean" },
"membershipNumber": { "type": "string" }
},
"if": {
"properties": { "isMember": { "const": true } }
},
"then": {
"required": ["membershipNumber"]
},
"else": {
"properties": {
"membershipNumber": { "minLength": 15 }
}
}
}
The if schema checks the isMember value. If the value is true, the then schema requires the membershipNumber. If the value is not true, the else schema applies the minLength constraint . The if/then/else is the mechanism for the value-based conditions, and the dependentRequired is the mechanism for the presence-based conditions.
Complete Example Session
// ============================================
// PART 1: THE REQUIRED KEYWORD
// ============================================
{
"type": "object",
"properties": {
"name": { "type": "string" },
"email": { "type": "string" }
},
"required": ["name", "email"]
}
// The name and the email are required.
// The missing property is the invalid instance.
// ============================================
// PART 2: THE PRESENCE VS THE VALUE
// ============================================
{
"type": "object",
"properties": {
"name": { "type": ["string", "null"] }
},
"required": ["name"]
}
// The null value is valid because the property is present.
// The properties schema allows the null.
// ============================================
// PART 3: THE NON-OBJECT INSTANCE
// ============================================
{
"required": ["name"]
}
// The "Hello World" is valid against the keyword.
// The type: "object" is the restriction.
// ============================================
// PART 4: THE STRICT OBJECT
// ============================================
{
"type": "object",
"properties": { "name": { "type": "string" } },
"required": ["name"],
"additionalProperties": false
}
// The extra property is rejected.
// The required and the additionalProperties are the strictness.
// ============================================
// PART 5: THE DEPENDENT REQUIRED
// ============================================
{
"type": "object",
"properties": {
"creditCardNumber": { "type": "string" },
"address": { "type": "string" }
},
"dependentRequired": {
"creditCardNumber": ["address"]
}
}
// The creditCardNumber requires the address.
// The address alone is valid.
// ============================================
// PART 6: THE IF-THEN-ELSE
// ============================================
{
"type": "object",
"properties": {
"isMember": { "type": "boolean" },
"membershipNumber": { "type": "string" }
},
"if": {
"properties": { "isMember": { "const": true } }
},
"then": {
"required": ["membershipNumber"]
}
}
// The isMember: true requires the membershipNumber.
// The isMember: false does not.
// ============================================
// PART 7: THE AJV VALIDATION
// ============================================
import Ajv from "ajv";
const ajv = new Ajv();
const validate = ajv.compile(schema);
const valid = validate(data);
if (!valid) console.log(validate.errors);
// The Ajv compiles the schema to a function.
// The validate.errors is the failure details.
// ============================================
// PART 8: THE TYPESCRIPT TYPE
// ============================================
import { JSONSchemaType } from "ajv";
interface User {
name: string;
email: string;
}
const schema: JSONSchemaType<User> = {
type: "object",
properties: {
name: { type: "string" },
email: { type: "string" }
},
required: ["name", "email"],
additionalProperties: false
};
// The JSONSchemaType is the typed schema.
// The required is the type-checked array.
// ============================================
// PART 9: THE ERROR MESSAGE
// ============================================
// The Ajv error for the missing property:
// {
// "instancePath": "",
// "schemaPath": "#/required",
// "keyword": "required",
// "params": { "missingProperty": "email" },
// "message": "must have required property 'email'"
// }
// The missingProperty is the field name.
// ============================================
// PART 10: THE CHECKLIST
// ============================================
// [ ] The required is an array of unique strings.
// [ ] The required is independent of the properties.
// [ ] The null value is allowed by the properties schema.
// [ ] The dependentRequired is used for the presence-based conditions.
// [ ] The if-then-else is used for the value-based conditions.
// [ ] The Ajv validates the instance.
// [ ] The JSONSchemaType types the schema.
The ten parts covered the required keyword, the presence vs the value, the non-object instance, the strict object, the dependentRequired, the if-then-else, the Ajv validation, the TypeScript type, the error message, and the checklist. The through-line is that the required is the presence assertion, and the conditional keywords are the refinement.
Quick Reference
The presence keywords
| Keyword | Applies to | Purpose |
|---|---|---|
required | Object | The required property names |
dependentRequired | Object | The conditional required properties |
properties | Object | The property subschemas |
additionalProperties | Object | The unspecified property policy |
The conditional keywords
| Keyword | Condition | Effect |
|---|---|---|
dependentRequired | A property is present | The listed properties are required |
if/then/else | A subschema validates | The then or the else applies |
dependentSchemas | A property is present | The subschema applies |
The Ajv usage
| Step | Code |
|---|---|
| Import | import Ajv from "ajv" |
| Create | const ajv = new Ajv() |
| Compile | const validate = ajv.compile(schema) |
| Validate | const valid = validate(data) |
| Errors | console.log(validate.errors) |
The error object
| Property | Purpose |
|---|---|
instancePath | The path to the invalid instance |
schemaPath | The path to the failed schema |
keyword | The failed keyword |
params.missingProperty | The missing field name |
message | The human-readable message |
Best Practices
โ Do This:
// Use the required for the mandatory fields
{ "required": ["name", "email"] } // โ
// Use the dependentRequired for the presence-based conditions
{ "dependentRequired": { "creditCardNumber": ["address"] } } // โ
// Use the additionalProperties: false for the strict objects
{ "additionalProperties": false } // โ
โ Don’t Do This:
// Use the required without the type: "object"
{ "required": ["name"] } // the non-object instance is valid // โ
// Assume the required checks the value
{ "required": ["name"] } // the null value is valid // โ
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| The non-object instance passes | The type: "object" is missing | Add the type: "object" |
The null value fails | The properties schema does not allow the null | Add the null to the type |
| The conditional does not trigger | The dependentRequired is used for the value condition | Use the if/then/else |
| The extra property is accepted | The additionalProperties is missing | Add the additionalProperties: false |
Real-World Examples
1. The required array
{ "required": ["name", "email"] }
2. The presence vs value
{ "required": ["name"], "properties": { "name": { "type": ["string", "null"] } } }
3. The strict object
{ "type": "object", "required": ["name"], "additionalProperties": false }
4. The dependentRequired
{ "dependentRequired": { "creditCardNumber": ["address"] } }
5. The if-then
{ "if": { "properties": { "isMember": { "const": true } } }, "then": { "required": ["membershipNumber"] } }
6. The Ajv compile
const validate = ajv.compile(schema);
7. The Ajv validate
const valid = validate(data);
8. The error access
console.log(validate.errors);
9. The JSONSchemaType
const schema: JSONSchemaType<User> = { ... };
10. The missing property
{ "params": { "missingProperty": "email" } }
Visual
The Required Keyword
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ THE REQUIRED KEYWORD โ
โ โ
โ The schema โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ required: โ The presence assertion โ
โ โ ["name", "email"] โ โ
โ โโโโโโโโโโโโฌโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ The instance has โ Both properties present โ
โ โ name and email โ โ valid โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ The instance has name only โ invalid (the email is missing)โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The Presence vs the Value
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ THE PRESENCE IS NOT THE VALUE โ
โ โ
โ {"name": null} โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ The required: โ The name is present โ
โ โ ["name"] โ โ valid โ
โ โโโโโโโโโโโโฌโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ The properties: โ The null is allowed โ
โ โ name: type null โ โ valid โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The Conditional Keywords
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ THE CONDITIONAL KEYWORDS โ
โ โ
โ dependentRequired: โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ The property is โ The presence condition โ
โ โ present โ โ
โ โโโโโโโโโโโโฌโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ The other property โ The dependent required โ
โ โ is required โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ if-then-else: โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ The value matches โ The value condition โ
โ โ the if schema โ โ
โ โโโโโโโโโโโโฌโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโดโโโโโโโ โ
โ โผ โผ โ
โ โโโโโโโโโโ โโโโโโโโโโ โ
โ โ then โ โ else โ โ
โ โโโโโโโโโโ โโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Item | Value |
|---|---|
required | The array of the required property names |
| The check | The presence, not the value |
| The non-object | Valid against the keyword |
dependentRequired | The presence-based conditional |
if/then/else | The value-based conditional |
| The Ajv | The validator that compiles the schema |
| The error | The missingProperty in the params |
Key takeaways:
- The
requiredkeyword is the presence assertion. It takes an array of unique strings, and an object is valid if every string is a property name in the instance, regardless of the value . - The presence is not the value. A
nullproperty passes therequiredcheck if thepropertiesschema allows thenulltype. Therequiredand thepropertiesare the separate concerns . - The
requiredkeyword applies only to the object instances. A non-object instance is valid against the keyword, and thetype: "object"is the mechanism that restricts the instance to the objects . - The
dependentRequiredis the presence-based conditional. It requires a list of properties when a specific property is present, and the condition is the presence, not the value . - The
if/then/elseis the value-based conditional. It applies a subschema when the instance validates against theifschema, and thethenor theelseis the conditional branch .
Remember: The required keyword is the presence assertion, and it is independent of the properties keyword. The dependentRequired is the conditional presence, and the if/then/else is the conditional value. The additionalProperties is the strictness policy, and the type: "object" is the restriction. The Ajv is the validator, and the validate.errors is the failure detail.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!