Skip to content

fix/examples: Fix OpenAPI "examples" - #935

Open
gevalo1 wants to merge 3 commits into
fastify:mainfrom
gevalo1:fix/examples
Open

fix/examples: Fix OpenAPI "examples"#935
gevalo1 wants to merge 3 commits into
fastify:mainfrom
gevalo1:fix/examples

Conversation

@gevalo1

@gevalo1 gevalo1 commented Jul 29, 2026

Copy link
Copy Markdown

Note: AI-assisted (Claude Opus 5) while writing, human-reviewed and human-tested.

What this fixes

examples arrays are always rewritten into the OpenAPI 3.0 shape, even when the
document declares openapi: 3.1.0.

In 3.0 that rewrite is correct and necessary: the Schema Object there is a subset
of JSON Schema draft 4 and only has a singular example, while examples is a
map of Example Objects that lives on the Media Type or Parameter Object. So a
single example is moved into example, and multiple examples become a named map
one level up.

In 3.1 the Schema Object is JSON Schema 2020-12. examples is a plain array,
valid at any depth, and example is deprecated. Running the 3.0 rewrite against a
3.1 document therefore is not correct:

  • a deprecated keyword is emitted where the modern one was already valid
  • values are silently dropped, only examples[0] survives
  • the output is inconsistent, because schemas the recursion does not reach (for
    example anything under anyOf/oneOf) keep their arrays, so one document ends
    up mixing both styles

This caused issues for us: we generate a 3.1 document for our REST docs and
were using a pnpm patch for this plugin to get a valid OpenAPI schema.

What I changed

The example handling now takes the target version into account. For 3.1.x the
arrays are left exactly as written, at every depth, and nothing is hoisted. Every
other version keeps the current behaviour, unchanged and untouched.

There is no new plugin option. The version already selects this, which matches what was asked for in #625:

We should add support to OAS v3.1 too but keep the v3.0 line as-is

We differentiate with an option to the plugin, check with how swagger vs oas are handled

x-examples is deliberately untouched and keeps producing the named Example Object
map in both versions, so the use case from #616 and #770 still works. That remains
the way to attach names, summaries and descriptions to examples.

The first commit is separate

While testing we found that parameter schemas were converted with schemaToMedia
instead of schemaToMediaRecursive, so nested examples arrays inside a parameter
were never resolved. An array query parameter like this:

querystring: {
  type: 'object',
  properties: {
    tags: { type: 'array', items: { type: 'string', examples: ['a', 'b'] } }
  }
}

Checklist

@gevalo1

gevalo1 commented Jul 29, 2026

Copy link
Copy Markdown
Author

Just for reference, this is what our pnpm patch looks like currently, so that we get a valid OpenAPI 3.1 schema:

diff --git a/lib/spec/openapi/utils.js b/lib/spec/openapi/utils.js
index 164cf1b05edfcbeeca352ac8fbb3f13fb09a6902..1ab5bed2c9c04a507a13740d88402fefd402f799 100644
--- a/lib/spec/openapi/utils.js
+++ b/lib/spec/openapi/utils.js
@@ -223,12 +223,12 @@ const schemaTypeToNestedSchemas = {
 }
 
 function resolveSchemaExamples (schema) {
-  const example = schema[xExamples] ?? schema.examples?.[0]
-  if (typeof example !== 'undefined') {
-    schema.example = example
-  }
-  delete schema[xExamples]
-  delete schema.examples
+  // const example = schema[xExamples] ?? schema.examples?.[0]
+  // if (typeof example !== 'undefined') {
+  //   schema.example = example
+  // }
+  // delete schema[xExamples]
+  // delete schema.examples
 }
 
 function resolveSchemaExamplesRecursive (schema) {
@@ -244,8 +244,8 @@ function schemaToMedia (schema) {
   const media = { schema }
 
   if (schema.examples?.length === 1) {
-    media.example = schema.examples[0]
-    delete schema.examples
+    // media.example = schema.examples[0]
+    // delete schema.examples
   } else if (schema.examples?.length > 1) {
     media.examples = convertExamplesArrayToObject(schema.examples)
     // examples is invalid property of media object schema

@gevalo1
gevalo1 marked this pull request as ready for review July 29, 2026 13:25
Parameter schemas were converted with schemaToMedia, which only resolves
examples at the top level of a schema. An examples array nested inside a
parameter schema, for example the items of an array query parameter, was
therefore left as it is and ended up in the document, where examples is not
a valid Schema Object keyword, making the specification invalid.

Parameters now use schemaToMediaRecursive, as request bodies and responses
already do.
The Schema Object in OpenAPI 3.1 is a JSON Schema, where examples is an
array of values and example is deprecated. The examples array was still
rewritten into the OpenAPI 3.0 shape though, so a single example was moved
into example and multiple examples were converted into a named examples map
on the media or parameter object, which loses values and emits a deprecated
keyword.

The version is now taken into account, as requested in
fastify#625 (comment),
so that a document targeting 3.1.x keeps the arrays untouched at every depth,
while the 3.0 line keeps its current behaviour. The x-examples field is
unchanged and remains the way to document named examples in both versions.
@gevalo1

gevalo1 commented Aug 13, 2026

Copy link
Copy Markdown
Author

Hi @mcollina, @Fdawgs, @climba03003, @Tony133
(Sorry for the ping)

Would appreciate a review when someone has time, happy to discuss and/or make any requested changes!

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates OpenAPI example handling to preserve JSON Schema examples arrays in OpenAPI 3.1.

Changes:

  • Makes example conversion version-aware.
  • Recursively handles nested parameter examples.
  • Adds tests and documentation for OpenAPI 3.1 behavior.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
lib/spec/openapi/utils.js Implements version-aware example handling.
test/spec/openapi/option.test.js Tests nested and OpenAPI 3.1 examples.
README.md Documents version-specific behavior.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread README.md
```json
"/": {
"post": {
"requestBody": {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants