| | |

JSON 07 ๐Ÿ“„ Validating Objects and Required Fields

โฑ๏ธ Estimated read: 11 min | |

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

KeywordApplies toPurpose
requiredObjectThe required property names
dependentRequiredObjectThe conditional required properties
propertiesObjectThe property subschemas
additionalPropertiesObjectThe unspecified property policy

The conditional keywords

KeywordConditionEffect
dependentRequiredA property is presentThe listed properties are required
if/then/elseA subschema validatesThe then or the else applies
dependentSchemasA property is presentThe subschema applies

The Ajv usage

StepCode
Importimport Ajv from "ajv"
Createconst ajv = new Ajv()
Compileconst validate = ajv.compile(schema)
Validateconst valid = validate(data)
Errorsconsole.log(validate.errors)

The error object

PropertyPurpose
instancePathThe path to the invalid instance
schemaPathThe path to the failed schema
keywordThe failed keyword
params.missingPropertyThe missing field name
messageThe 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

PitfallWhy It HappensFix
The non-object instance passesThe type: "object" is missingAdd the type: "object"
The null value failsThe properties schema does not allow the nullAdd the null to the type
The conditional does not triggerThe dependentRequired is used for the value conditionUse the if/then/else
The extra property is acceptedThe additionalProperties is missingAdd 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

ItemValue
requiredThe array of the required property names
The checkThe presence, not the value
The non-objectValid against the keyword
dependentRequiredThe presence-based conditional
if/then/elseThe value-based conditional
The AjvThe validator that compiles the schema
The errorThe missingProperty in the params

Key takeaways:

  • The required keyword 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 null property passes the required check if the properties schema allows the null type. The required and the properties are the separate concerns .
  • The required keyword applies only to the object instances. A non-object instance is valid against the keyword, and the type: "object" is the mechanism that restricts the instance to the objects .
  • The dependentRequired is 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/else is the value-based conditional. It applies a subschema when the instance validates against the if schema, and the then or the else is 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.

Was this helpful?


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!