middleware icon indicating copy to clipboard operation
middleware copied to clipboard

@hono/zod-openapi outputs examples as an array, causing incompatibility with Swagger UI

Open k3ntar0 opened this issue 1 year ago • 2 comments

Summary

According to the OpenAPI specification, the examples field in a response must be an object. However, when using Schema.openapi({ examples }), if examples is provided as an array, it is output as an array in the generated OpenAPI spec. This causes Swagger UI to fail to parse the examples correctly. Additionally, the examples provided in the schema are not displayed at all in Swagger UI.

Implementation

import { OpenAPIHono, createRoute, z } from "@hono/zod-openapi";

const ResourceOutputSchema = z
  .object({
    status: z.enum(["pending", "completed"]),
  })
  .openapi({
    description: "The status of the resource",
    examples: [{ status: "pending" }, { status: "completed" }],
  });

const apiDefinition = createRoute({
  method: "get",
  path: "/resource/:id",
  request: {
    params: z.object({
      id: z.string(),
    }),
  },
  responses: {
    200: {
      description: "Successful response",
      content: {
        "application/json": {
          schema: ResourceOutputSchema,
        },
      },
    },
  },
});

export const getResourceRoute = (app: OpenAPIHono) => {
  return app.openapi(apiDefinition, async (c) => {
    return c.json({ status: "completed" as const }, 200);
  });
};

Generated OpenAPI Spec

{
  "openapi": "3.1.0",
  "info": {
    "version": "1.0.0",
    "title": "Example API"
  },
  "components": { "schemas": {}, "parameters": {} },
  "paths": {
    "/resource/:id": {
      "get": {
        "parameters": [
          {
            "schema": { "type": "string" },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": ["pending", "completed"]
                    }
                  },
                  "required": ["status"],
                  "description": "The status of the resource",
                  "examples": [
                    { "status": "pending" },
                    { "status": "completed" }
                  ]
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {}
}

The examples provided in the schema are not displayed in Swagger UI:

Screenshot 2024-12-10 at 23 29 40

Additional Notes

If my understanding is incorrect and examples being an array is intended behavior, I’d appreciate clarification. Thank you!

k3ntar0 avatar Dec 10 '24 14:12 k3ntar0

I have same issue

noworker avatar Dec 26 '24 07:12 noworker

Hi @k3ntar0

This may be zod-to-openapi issue that is used in @hono/zod-openapi. For example, you can use it without @hono/zod-openapi and reproduce this issue:

import { extendZodWithOpenApi, OpenAPIRegistry, OpenApiGeneratorV3 } from '@asteasolutions/zod-to-openapi'
import { z } from 'zod'

extendZodWithOpenApi(z)

const registry = new OpenAPIRegistry()

registry.register(
  'Resource',
  z
    .object({
      status: z.enum(['pending', 'completed'])
    })
    .openapi({
      description: 'The status of the resource',
      examples: [{ status: 'pending' }, { status: 'completed' }]
    })
)

const generator = new OpenApiGeneratorV3(registry.definitions)
const doc = generator.generateDocument({
  openapi: '3.0.0',
  info: {
    version: '1.0.0',
    title: 'My API'
  }
})

console.log(JSON.stringify(doc, null, 2))

The output (examples will be array):

{
  "openapi": "3.0.0",
  "info": {
    "version": "1.0.0",
    "title": "My API"
  },
  "components": {
    "schemas": {
      "Resource": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "completed"
            ]
          }
        },
        "required": [
          "status"
        ],
        "description": "The status of the resource",
        "examples": [
          {
            "status": "pending"
          },
          {
            "status": "completed"
          }
        ]
      }
    },
    "parameters": {}
  },
  "paths": {}
}

Can you raise an issue about it on zod-to-openapi repo? They will help us.

yusukebe avatar Jan 05 '25 10:01 yusukebe