Skip to main content

Back to the liblab blog

OpenAPI oneOf, allOf, anyOf: Understanding the Differences

| 5 min

Crafting precise and adaptable schemas is paramount in API design. OpenAPI offers several constructs to help define flexible and robust data models, the most powerful and flexible of which are oneOf, allOf, and anyOf. These keywords provide different ways to validate data against multiple schemas, making them essential for designing APIs that accommodate varied data structures while maintaining type safety.

Whether you’re designing your API using Laravel, Flask, Nest, Django, FastAPI, SpringBoot, Express, ASP.NET, or anything else. Understanding the key differences and how to use OpenAPI oneOf, allOf, and anyOf will help you design a more maintainable and future-proof API.

The Key Differences

  • oneOf – The data must validate against exactly one of the subschemas.
  • allOf – The data must validate against all the subschemas simultaneously.
  • anyOf – The data must validate against at least one (or more) of the subschemas.

Understanding these distinctions is crucial when designing OpenAPI definitions, as they directly impact SDK generation, data validation, and client-side type safety.

Understanding Subschemas

To understand these keywords, it’s first important to understand what a subschema is. At its simplest, a subschema is just a schema referenced in a larger, more complex schema. Take, for example, this basic schema that defines a Dog, Cat, and Animal. They are all schemas, but when Dog and Cat are referenced in Animal, they are subschemas of Animal.

This is similar to classes and subclasses, but the relationships are top-down rather than bottom-up. In this example, Dog does not inherit or extend from Animal and does not take on any of Animal’s attributes. Instead, Animal defines that Dog or Cat as possible subschemas that it can conform to.

components:
schemas:
Dog: # ...
Cat: # ...
Animal:
oneOf:
- $ref: '#/components/schemas/Dog'
- $ref: '#/components/schemas/Cat

OpenAPI oneOf: Ensuring Exclusive Schema Matching

Use OpenAPI oneOf when a value should conform to precisely one of the provided schemas. This is particularly useful when defining mutually exclusive options.

Example:

components:
schemas:
Pet:
type: object
properties:
name:
type: string
petType:
type: string
discriminator:
propertyName: petType
Dog:
allOf:
- $ref: '#/components/schemas/Pet'
- type: object
properties:
bark:
type: boolean
dig:
type: boolean
Cat:
allOf:
- $ref: '#/components/schemas/Pet'
- type: object
properties:
meow:
type: boolean
scratch:
type: boolean
Animal:
oneOf:
- $ref: '#/components/schemas/Dog'
- $ref: '#/components/schemas/Cat'

Here, an Animal must be either a Dog or a Cat, but not both. This ensures that API consumers provide and receive well-structured data that conforms to differing expectations of Cat and Dog. For example, a user can provide a Dog that can bark and dig but cannot meow.

Pitfall: One common issue with oneOf is its strict validation. oneOf is exactly one. If none or more than one schema matches, validation will fail. While the “none case” is expected, the “more than one case” can confuse API consumers and developers.

OpenAPI allOf: Merging Multiple Schemas

Use OpenAPI allOf to create composite objects that inherit properties from multiple schemas. This is useful when reusing common schema elements while adding specific properties.

Example:

components:
schemas:
Address:
type: object
properties:
street:
type: string
city:
type: string
Person:
type: object
properties:
name:
type: string
age:
type: integer
Employee:
allOf:
- $ref: '#/components/schemas/Person'
- $ref: '#/components/schemas/Address'
- type: object
properties:
employeeId:
type: string

Here, Employee is a combination of Person and Address, inheriting all their properties while adding employeeId. This is especially useful when generating SDKs, as the resulting type automatically includes all inherited fields. Any update to the definition of Person or Address will be automatically reflected in Employee.

Pitfall: Overusing allOf can create deeply nested structures that become difficult to manage and debug, especially when resolving conflicts between inherited properties.

OpenAPI anyOf: Flexible Validation Against Multiple Schemas

Use OpenAPI anyOf when a value should match at least one (but possibly more) of the subschemas. This is useful for handling partial matches.

Example:

components:
schemas:
Contact:
anyOf:
- type: object
properties:
email:
type: string
format: email
- type: object
properties:
phone:
type: string

Here, a Contact object must contain either an email, a phone, or both. This is useful for more flexible schemas where multiple data input formats are acceptable.

Pitfall: With anyOf, partial matches can lead to unintended behaviors if API consumers expect all properties always to be present. Defining required fields can mitigate this.

To effectively use OpenAPI’s oneOf, allOf, and anyOf, consider these best practices:

  • Document Clearly: Provide detailed documentation on how your API behaves to prevent confusion for API consumers.
  • Use Discriminators with oneOf: Implement a discriminator property to simplify schema resolution and avoid ambiguous validation errors.
  • Avoid Excessive Nesting with allOf: While allOf promotes schema reuse, excessive inheritance can lead to deeply nested structures that are difficult to maintain.
  • Define Required Fields for anyOf: If a schema under anyOf should always have a specific property, explicitly set it as required to avoid unexpected validation issues.
  • Test Thoroughly: Validate your OpenAPI specification using tools like liblab validate or Linters to catch misconfigurations early.

Implications for SDK Generation

Choosing between oneOf, allOf, and anyOf has a significant impact on how SDKs handle type definitions:

  • OpenAPI oneOf ensures that only one type is valid at a time, leading to strict type-checking.
  • OpenAPI allOf merges schemas, making SDKs generate fully composed types with inherited properties.
  • OpenAPI anyOf allows looser type enforcement, enabling SDKs to accept multiple valid structures.

Understanding these nuances helps design API contracts that are flexible and well-defined, ensuring a better developer experience and more reliable SDKs.

Conclusion

By effectively leveraging oneOf, allOf, and anyOf, API designers can create expressive and maintainable OpenAPI schemas, enhancing the developer experience across various integrations. Whether building SDKs or validating API responses, choosing the proper construct ensures your API remains robust and easy to consume.


Before you go, check out our CLI tool that can automatically generate client libraries in 6+ languages for any API.Build an SDK For Any API

SDK Generator

OpenAPI