# How to use `allOf` in OpenAPI Use of `allOf` comes from the desire for reuse. When you have a single source of truth, maintenance is easier. This makes sense. You might want to reuse a lot of things. But `allOf` is not appropriate in many cases and can result in illogical schemas. How do you know when to use `allOf` and when to avoid it? This article covers: - how to use `allOf` - how `allOf` is evaluated - valid use cases - common language patterns that warn that `allOf` use is not appropriate ## Usage of `allOf` Declare `allOf` as an array of schemas. > All of these keywords must be set to an array, where each item is a schema. This works in YAML. ```yaml allOf: - title: time type: object properties: time: type: string - title: date type: object properties: date: type: string ``` And it works in JSON. The remainder of this article uses YAML for schema definitions. ```json { "allOf": [ { "title": "time", "type": "object", "properties": { "time": { "type": "string" } } }, { "title": "date", "type": "object", "properties": { "date": { "type": "string" } } } ] } ``` ## Evaluation of `allOf` A goal of JSON Schema is to be able to evaluate if JSON is valid or invalid with the defined schema. From the definition of `allOf`, it is treated like a logical `AND`: > Must be valid against *all* of the subschemas ```js $time && $date ``` Based on our prior `allOf` declaration which requires `time` and `date` schemas, the following JSON would match the schemas: ```json { "time": "08:15:00+06:00", "date": "2022-01-22" } ``` Does the following JSON match the `allOf` too? ```json { "date": "2022-01-22" } ``` The "time" property is missing, and you may think that it only matches the `date` schema. However, neither schema, including the `time` schema, has any required properties. Therefore, it matches all of the schemas. In the same way, the following JSON matches the `allOf` schemas too. ```json { "temperature": 25, "unit": "C" } ``` That doesn't seem right. But it is. The schema declares what some properties types must be if they are present. It didn't declare them as required. The following schema is invalid, because `date` is not a `string`. ```json { "temperature": 25, "unit": "C", "date": 22 } ``` If you declare a media type examples in your OpenAPI definition, and turn on the [no-invalid-media-type-examples rule](/docs/cli/rules/oas/no-invalid-media-type-examples), Redocly evaluates the examples against the schema to help you evaluate them. You can also do this be evaluating real API responses with API testing. [Contact us](/contact-us) if you're interested in doing that. ## Valid cases There are times when schemas are a combination of two pre-existing schemas. If you find yourself wanting to add "with minor exceptions", then do not use the `allOf` keyword, no matter how tempting. For example, let's say you have a resource for User. ```yaml title: User required: - id - email type: object properties: id: type: string name: type: string email: type: string avatar: type: string phone: type: string dob: type: string createdAt: type: string recentLogInAt: type: string ``` And then you have other resources that use an excerpt of the `User` schema such as `id`, `name`, `email`, and `avatar`. (The topic of **API design** is different from the topic of **API description**, and this article doesn't cover if you *should* design an API this way.) In order to reuse that excerpt of the User schema, you could rework the schema as follows. ```yaml title: UserExcerpt required: - id - email type: object properties: id: type: string name: type: string email: type: string avatar: type: string ``` Then, you could use that in the User and any other schemas with `allOf`. ```yaml title: User allOf: - $ref: '#/components/schemas/UserExcerpt' - type: object properties: phone: type: string dob: type: string createdAt: type: string recentLogInAt: type: string ``` And another schema could reuse it similarly. ```yaml title: ParkingSpot allOf: - $ref: '#/components/schemas/UserExcerpt' - type: object properties: licenseExpiration: type: string spotId: type: string ``` ### Siblings to `$ref`s OpenAPI 3.0 has a limitation related to reuse. Schemas have some properties that are informational and do not impact the validation of JSON. Two of those properties are `summary` and `description`. A common use case is the desire to reuse the schema by change the description due to the context. OpenAPI 3.1 allows for siblings next to the `$ref`. ```yaml type: object properties: transactionId: $ref: '#/components/schemas/ResourceId' description: ID of the transaction. ``` OpenAPI 3.0 and prior do not allow for siblings next to the `$ref`, but the `allOf` keyword could be used above it as a "workaround". ```yaml type: object properties: transactionId: description: ID of the transaction. allOf: - $ref: '#/components/schemas/ResourceId' ``` This behavior confuses people, and some people think of it as a way to override the reference object properties. This is only for informational properties and not for properties that are used for evaluation. ## Illogical schemas from `allOf` misuse Word to watch out for that could indicate misuse: - override - extend ### Type override is invalid Overriding a description and summary is allowed. From an evaluation perspective, it works because the description is going to match any type. The following example references a `ResourceId` schema and its type is a `string`. Therefore, the following example is illogical because `transactionId` cannot be an `integer` **and** a `string`. ```yaml type: object properties: transactionId: description: ID of the transaction. type: integer allOf: - $ref: '#/components/schemas/ResourceId' ``` ### Different types are invalid The following example demonstrates illogical schemas where types mismatch within a list of schemas provided to the `allOf` keyword. ```yaml allOf: - $ref: '#/components/schemas/Foo' - $ref: '#/components/schemas/Bar' ``` If `Foo` and `Bar` are not of the same type, then the logical `AND` cannot be true. For example, something cannot be a `string` and an `object` at the same time. Sometimes, this is more difficult to notice when using reference objects. However, it's clear when written the following way. ```yaml allOf: - type: string - type: object ``` ### Closed schemas and `allOf` are invalid Even when the schemas are of the same type, there can still be illogical conflicts when using the `allOf` keyword. The following schema demonstrates and illogical conflict. ```yaml allOf: - type: object properties: date: type: string additionalProperties: false - type: object properties: time: type: string additionalProperties: false ``` The `additionalProperties: false` means that the schema cannot have any additional properties. The following is invalid, because it matches the first schema, but the second schema does not have `date` declared as a property and it declares there cannot be any additional properties. ```json { "date": "2022-01-22" } ``` ## Summary In Swagger 2.0 or OpenAPI 3.0, use `allOf` to override a `description` or `summary` of a schema. In OpenAPI 3.1, use a sibling to the `$ref` to override a `description` or `summary`. Do **NOT** override any other properties including `type`. Use the `allOf` keyword as a logical `AND`. Be aware of common illogical combinations: - mismatched types - schemas where additional properties are not allowed The `allOf` keyword may be used with [the discriminator](/learn/openapi/discriminator). Also, `allOf` is almost always used with at least one [reference object](/learn/openapi/ref-guide). Consider cases where the schema is the same with one minor exception as a possible design problem. Refactoring to use `allOf` may not be a good idea for those scenarios.