Lesson 5: Combining Schemas
"allOf" requires the data to satisfy every sub-schema in the array simultaneously, useful for merging requirements. "anyOf" requires the data to match at least one sub-schema, while "oneOf" requires it to match exactly one. The "if/then/else" keywords enable conditional validation: if the data satisfies the "if" schema, the "then" schema is also applied; otherwise the "else" schema is applied. This is particularly powerful for modeling discriminated unions like different payment method shapes.
Why This Matters
Real-world data is rarely one uniform shape — a payment can be a card, a bank transfer, or cash, each needing different required fields; a webhook event can be one of a dozen event types with a shared envelope but a different payload. Without combinators, you'd either write one loose schema that fails to catch shape-specific mistakes, or maintain the conditional logic by hand in application code, disconnected from the schema. "if/then/else" and "oneOf" let you express these discriminated unions directly in the schema, so a single JSON Schema document can validate an entire family of related shapes and stay the single source of truth for API documentation and generated types.
Common Mistakes
- ✕ Reaching for "anyOf" when the branches are meant to be mutually exclusive."anyOf" happily accepts data that matches more than one branch, which can hide genuinely ambiguous data. If exactly one shape should ever apply — like exactly one payment method — use "oneOf" so overlapping matches become a validation error instead of passing silently.
- ✕ Combining "allOf" (often via $ref) with "additionalProperties": false in one of the branches.This is the classic "allOf + additionalProperties" gotcha: each sub-schema in "allOf" is evaluated independently, so a branch with additionalProperties: false will reject properties defined by a sibling branch, even though the merged shape looks like it should allow them. Prefer applying additionalProperties: false once, at the outermost schema, rather than inside an allOf branch.
- ✕ Nesting if/then/else many levels deep with no final else.Without a trailing else, data that matches none of the "if" conditions gets no extra constraints applied at all, which can silently let invalid combinations through. Always account for the "none of the above" case explicitly.
- ✕ Expecting a single, simple error message when "oneOf" fails.When none of the oneOf branches match, most validators report one set of errors per branch, which can look like a wall of noise. Read it as "here's why each option failed" rather than one linear error.
Challenge
Change paymentType to 'bank_transfer'. Notice cardNumber is no longer required but accountNumber is. Fix the data.