OpenAPI v3.1 and JSON Schema

OpenAPI v3.1 and JSON Schema

Updated 2019-05-29: OpenAPI v3.1 has dropped SemVer, so a few things that were going to be deprecated are now just straight up removed. This post has been updated to reflect that.

Updated 2021-01-10: JSON Schema released Draft 2020-12 which clarified a few edge cases, it's not a meaningful change to the content of the article but we've replaced references to 2020-12 to avoid future confusion.

OpenAPI using an "extended subset" of JSON Schema has caused confusion in the API space since before I had even heard of OpenAPI. Thanks to a lot of hard work from a lot of people over the last 6 months, OpenAPI v3.1 will in fact solve this problem, and there was much rejoicing.

For anyone who's not read a million words on the topic from me on this topic already, I was talking about the divergence in 2017, 2018, creating workarounds, suggesting functionality, complaining about that functionality... Everyone in and around OpenAPI was trying to get something done to solve this problem. For all this time, often unaware of any of the discussions, the divergence was a constant pain in everyone's ass: tooling vendors and end-users alike.

Well, good news. OpenAPI v3.1 has brought along some substantial change. For a while we thought we'd see a few of OpenAPI-special keywords deprecated, and the JSON Schema keywords and functionality supported, but this "two ways of doing something" was not looking good for tooling vendors. So, after much consideration, OpenAPI v3.1 has dropped SemVer in order to more quickly and easily properly align itself with JSON Schema. Here's an overview:

  1. OpenAPI Schema is a vocabulary of JSON Schema 2020-12
  2. type arrays are now supported
  3. nullable has been removed
  4. arbitrary keywords are now supported without needing an x- on the front
  5. exclusiveMinimum and exclusiveMaximum take a numeric value instead of being a boolean
  6. discriminator is deprecated, and miiiight still be removed before OpenAPI v3.1 final happens

Let's look at some of this a bit closer.

OpenAPI Schema is a Vocabulary of JSON Schema 2020-12

Check out this new wording from the OpenAPI Specification.

The OpenAPI Schema Object is a JSON Schema vocabulary which extends JSON
Schema Core and Validation vocabularies. As such any keyword available for those
vocabularies is by definition available in OpenAPI, and will work the exact same
way.

Booyeah. OpenAPI no longer defines a complex list of discrepancies, it simply says that the Schema Object properties are defined in JSON Schema Core and JSON Schema Validation.

There are no more "missing" JSON Schema properties. If a keyword exists in JSON Schema Draft 2020-12 (Core or Validation), then it's good to use in OpenAPI v3.1.

How about keywords which worked... differently?

type can now be an array

The most common source of confusion for many people was that type could be an array of strings in JSON Schema, but had to be a single string in OpenAPI v2-3. In OpenAPI v3.0 you could at least use oneOf to select multiple types, meaning that three different ways to do the same thing were possible, with some tooling supporting one, two, or three of them...

OpenAPI v2.0

type: string
# no integers allowed... 🤷‍♂️

OpenAPI v3.0

oneOf:
  - type: string
  - type: integer

OpenAPI v3.1

type: [string, integer]

OpenAPI v3.1 is now happy to take a type array!

Users of languages like Java and C++ will still wonder why anyone would ever do this, but tooling vendors building tools for this can just treat it as they did a oneOf.

Users of languages like JavaScript, Ruby and PHP are happy it's now much easier to describe common functionality. Maybe you used API Evolution to change a string to an array of address components, maybe it could even be an object! Go nuts mate.

nullable is GONE

Nullable was deprecated for a while, which was... my favourite pull request I ever sent. Henry Andrews, author of JSON Schema and now OpenAPI contributor, got to take the cake. It's removed.

OpenAPI v3.0

type: string
nullable: true

OpenAPI v3.1

type: [string, "null"]

This is probably the biggest breaking change, but it's such an easy find and replace. You can even use tools like openapi-schema-to-json-schema to upgrade your schema files (so long as you've been keeping them DRY!)

Any format from JSON Schema is fine

JSON Schema Draft 2020-12 added a few new formats so go wild with those. There are some changes to how formats are interpreted now, and they are being relaxed from validation, to annotations which could be used for validation if configured to do so.

Basically, trying to validate that data follows the rules for what a specific validation tool things is valid for that format is... awful. Different tools disagree about what an email address is, despite there being a standard for what a valid email address is.

Tool vendors can feel free to keep existing format validation, but probably move it behind a config switch. Maybe the next major version of that tooling could have it default off.

Arbitrary Keywords

Every now and then somebody would add arbitrary keywords to their JSON Schema files to keep track of information that special (maybe in-house) tooling understood, but were not part of the JSON Schema spec. Then they'd try and use those JSON Schema files in OpenAPI, only to find it blows up. This was also fixed.

In addition to the JSON Schema properties defined in the vocabularies defined in the JSON Schema Core and JSON Schema Validation specifications, any properties can be used from any vocabularies, or entirely arbitrary keywords.

OpenAPI v3.0 would only allow "extensions", which were arbitrary keywords starting with x-. Now, no need for the x-, unless you like it, in which case carry on. 👍

example vs examples

JSON Schema has examples which is a bare array, which means you can have a string with two possible examples like this:

type: string
examples:
- squirtle
- charmander

Documentation tools could chose to show the first one, or both, doesn't matter they're both good choices.

OpenAPI v3.0 was a little different:

type: string
example: squirtle

JSON Schema based tooling would not know what example was, and OpenAPI tooling would choke when it saw examples because it is not a valid keyword inside the Schema Object.

In OpenAPI v3.1 the Schema Object example keyword is deprecated, so you should start using examples in your API description documents.

I will probably add a Spectral rule for that to help nudge people in the right direction.

examples are not examples

The change above does solve a discrepancy, but it does unfortunately introduce its own little bit of confusion. Hold onto your butts.

OpenAPI v3.0 Schema Objects do not have examples as a keyword, but there is a totally unrelated examples which shows up elsewhere in OpenAPI v3.0 (in the Media Type Object, for request and responses). Don't mix them up, they act differently.

Let's take a look at this not-in-the-schema approach to examples which OpenAPI offers:

parameters:
  - in: query
    name: limit
    schema:
      type: integer
      maximum: 50
    examples:       # Multiple examples
      zero:         # Distinct name
        value: 0    # Example value
        summary: A sample limit value  # Optional description
      max: # Distinct name
        value: 50   # Example value
        summary: A sample limit value  # Optional description

Example from Swagger.io talking about Examples.

For any parameters, requestBody or responses, outside of the Schema Object, there is a special OpenAPI keyword called examples. This is not a bare array like the JSON Schema examples, but an object where the keys are names, and the example has to go inside the value key.

This confusion of two types of examples is going to persist, but at least this problem can be solved with documentation and tutorials, instead of being a roadblock making files be unusable in some tooling but fine in others. 😅

exclusiveMinimum and exclusiveMaximum are different

JSON Schema made an improvement to how exclusiveMinimum and exclusiveMaximum work. In older versions, they were a boolean switch which would suggest that the minim value defined was also an acceptable value.

OpenAPI v3.0

# Value has to be 21 or over
minimum: 21

# Value has to be greater than 21, not 21
minimum: 21
exclusiveMinimum: true

OpenAPI v3.1

# Value has to be 21 or over (same)
minimum: 21

# Value has to be greater than 21, not 21 (new!)
exclusiveMinimum: 21

This is a small change, and has be done to simplify things. Keywords in schema objects generally should not effect other keywords in unexpected ways, so this keyword now just handles its own business.

More to Come

The work for JSON Schema alignment is pretty much done, but there are a few licks of paint until its completely sorted. These tweaks might come in over the course of the release candidate process, the first of which is supposedly going to be the end of February (this month?! 🤯)

OpenAPI v3.1 has a bunch of other cool stuff coming too:

  • Webhooks which do not have to be documented as callbacks underneath a specific operation
  • paths is now optional so an API description could be Webhooks only
  • Maybe overlays will be a thing

We'll talk about all this in future articles in the future. Until then, make sure your tooling is ready for JSON Schema 2020-12 and OpenAPI v3.1. Many tooling vendors are hard at work on this as we speak, with a few validation tools already done:

Head over to the official JSON Schema Implementations list of all sorts of tooling as they're released.

Hopefully soon this will all be behind us.