Skip to content

Nested Objects

Validno provides full support for validating complex nested object structures, allowing you to create comprehensive validation schemas for deeply nested data.

Basic Nested Validation

Define nested objects by creating sub-schemas within your main schema:

javascript
const schema = new Schema({
  user: {
    name: {
      type: String,
      required: true,
      rules: {
        lengthMin: 2,
        lengthMax: 50
      }
    },
    email: {
      type: String,
      required: true,
      rules: {
        isEmail: true
      }
    }
  }
});

const data = {
  user: {
    name: "John Doe",
    email: "john@example.com"
  }
};

const result = schema.validate(data);

Multi-Level Nesting

You can nest objects multiple levels deep:

javascript
const orderSchema = new Schema({
  order: {
    id: {
      type: String,
      required: true
    },
    customer: {
      profile: {
        firstName: {
          type: String,
          required: true,
          rules: {
            lengthMin: 2
          }
        },
        lastName: {
          type: String,
          required: true,
          rules: {
            lengthMin: 2
          }
        },
        contact: {
          email: {
            type: String,
            required: true,
            rules: {
              isEmail: true
            }
          },
          phone: {
            type: String,
            required: false,
            rules: {
              regex: /^\d{3}-\d{3}-\d{4}$/
            }
          }
        }
      },
      preferences: {
        newsletter: {
          type: Boolean,
          required: false
        },
        notifications: {
          type: Array,
          rules: {
            enum: ['email', 'sms', 'push']
          }
        }
      }
    }
  }
});

Nested Arrays with Objects

Validate arrays containing objects:

javascript
const schema = new Schema({
  items: {
    type: Array,
    eachType: Object,
    rules: {
      lengthMin: 1
    }
  }
});

// For validating the structure of objects within the array,
// you would typically validate the array first, then validate 
// each object separately with its own schema

Complex Example: E-commerce Order

javascript
const orderSchema = new Schema({
  orderId: {
    type: String,
    required: true,
    rules: {
      regex: /^ORD-\d{6}$/
    }
  },
  
  customer: {
    id: {
      type: Number,
      required: true,
      rules: {
        min: 1
      }
    },
    personalInfo: {
      firstName: {
        type: String,
        required: true,
        rules: {
          lengthMin: 2,
          lengthMax: 50
        }
      },
      lastName: {
        type: String,
        required: true,
        rules: {
          lengthMin: 2,
          lengthMax: 50
        }
      },
      email: {
        type: String,
        required: true,
        rules: {
          isEmail: true
        }
      }
    },
    address: {
      street: {
        type: String,
        required: true,
        rules: {
          lengthMin: 5
        }
      },
      city: {
        type: String,
        required: true,
        rules: {
          lengthMin: 2
        }
      },
      zipCode: {
        type: String,
        required: true,
        rules: {
          regex: /^\d{5}(-\d{4})?$/
        }
      },
      country: {
        type: String,
        required: true,
        rules: {
          enum: ['US', 'CA', 'UK', 'DE', 'FR']
        }
      }
    }
  },
  
  payment: {
    method: {
      type: String,
      required: true,
      rules: {
        enum: ['credit_card', 'debit_card', 'paypal']
      }
    },
    amount: {
      type: Number,
      required: true,
      rules: {
        min: 0.01
      }
    },
    currency: {
      type: String,
      required: true,
      rules: {
        enum: ['USD', 'EUR', 'GBP', 'CAD']
      }
    }
  },
  
  shipping: {
    method: {
      type: String,
      rules: {
        enum: ['standard', 'express', 'overnight']
      }
    },
    trackingNumber: {
      type: String,
      required: false
    }
  }
});

// Example valid order data
const orderData = {
  orderId: "ORD-123456",
  customer: {
    id: 1001,
    personalInfo: {
      firstName: "John",
      lastName: "Doe",
      email: "john.doe@example.com"
    },
    address: {
      street: "123 Main Street",
      city: "New York",
      zipCode: "10001",
      country: "US"
    }
  },
  payment: {
    method: "credit_card",
    amount: 99.99,
    currency: "USD"
  }
};

const result = orderSchema.validate(orderData);

Error Path Tracking

When nested validation fails, Validno provides clear error paths:

javascript
const schema = new Schema({
  user: {
    profile: {
      email: {
        type: String,
        rules: {
          isEmail: true
        }
      }
    }
  }
});

const invalidData = {
  user: {
    profile: {
      email: "not-an-email" // Invalid email
    }
  }
};

const result = schema.validate(invalidData);
// The error will clearly indicate the path: user.profile.email

Best Practices for Nested Validation

1. Keep Nesting Reasonable

Don't nest too deeply - it can become hard to manage:

javascript
// Good: 2-3 levels
const goodSchema = new Schema({
  user: {
    profile: {
      contact: {
        email: { type: String }
      }
    }
  }
});

// Avoid: Too deep
const avoidSchema = new Schema({
  app: {
    user: {
      profile: {
        personal: {
          contact: {
            primary: {
              email: { type: String }
            }
          }
        }
      }
    }
  }
});

2. Use Descriptive Field Names

javascript
const schema = new Schema({
  orderDetails: {
    customerInfo: {
      billingAddress: {
        streetAddress: { type: String },
        city: { type: String }
      }
    }
  }
});
javascript
const userSchema = new Schema({
  // Personal information
  personalInfo: {
    firstName: { type: String },
    lastName: { type: String },
    birthDate: { type: Date }
  },
  
  // Contact information
  contactInfo: {
    email: { type: String, rules: { isEmail: true } },
    phone: { type: String }
  },
  
  // Account settings
  accountSettings: {
    newsletter: { type: Boolean },
    privacy: { type: String, rules: { enum: ['public', 'private'] } }
  }
});

4. Consider Optional Groups

javascript
const profileSchema = new Schema({
  username: { type: String, required: true },
  
  // Optional social media links
  socialMedia: {
    twitter: { type: String, required: false },
    linkedin: { type: String, required: false },
    github: { type: String, required: false }
  }
});

Working with Dynamic Nested Data

For highly dynamic nested structures, consider validating in stages:

javascript
// First, validate the top-level structure
const topLevelSchema = new Schema({
  type: {
    type: String,
    rules: {
      enum: ['user', 'product', 'order']
    }
  },
  data: {
    type: Object,
    required: true
  }
});

// Then, based on the type, validate the nested data with specific schemas
const userDataSchema = new Schema({
  username: { type: String, required: true },
  email: { type: String, rules: { isEmail: true } }
});

const productDataSchema = new Schema({
  name: { type: String, required: true },
  price: { type: Number, rules: { min: 0 } }
});

Validation Tips

  1. Validate nested objects exist before validating their properties
  2. Use consistent field naming across nested levels
  3. Consider the user experience - nested errors should be clear
  4. Test edge cases like missing nested objects
  5. Keep validation logic focused - one concern per nested level

Next Steps

Released under the ISC License.