asteasolutions / zod-to-openapi

A library that generates OpenAPI (Swagger) docs from Zod schemas
MIT License
788 stars 52 forks source link

Use z.describe when called after optional, default, nullable #225

Closed kernwig closed 2 months ago

kernwig commented 2 months ago

After many months of using zod-to-openapi, I noticed that many of my hand crafted descriptions are not appearing in the documentation.

In PR #104 , you have a unit test with:

    const schema = registerSchema(
      'Test',
      z
        .object({
          type: z.string().describe('Just a type'),
          title: z.string().describe('Just a title').optional(),
        })
        .describe('Whole object')
    );

I'm requesting this addition to the unit test also succeed:

    const schema = registerSchema(
      'Test',
      z
        .object({
          type: z.string().describe('Just a type'),
          title: z.string().describe('Just a title').optional(),
          body: z.string().default("").describe('Just a body'),
        })
        .describe('Whole object')
    );

Right now, the description on "body" does not appear in the documentation. I've noticed this on .optional() and .nullable() too, when they are specified before .describe(). My team finds it more natural to tack the description on the end, not in the middle. You have to understand the inner workings of Zod to know which functions can be called before .describe and which have to be called after.

AGalabov commented 2 months ago

@kernwig I fully agree. I will take a look at it as soon as I can and get back to you.

kernwig commented 2 months ago

Thank you. I have also found .refine and .superRefine also lose the description.

AGalabov commented 2 months ago

@kernwig I tried adding the values to the test as you suggested.

Running the following minimal script:

import { z } from 'zod';
import { extendZodWithOpenApi } from './zod-extensions';
import { OpenApiGeneratorV3 } from './v3.0/openapi-generator';

extendZodWithOpenApi(z);

const schema = z
  .object({
    type: z.string().describe('Just a type'),
    title: z.string().describe('Just a title').optional(),
    body: z.string().default('').describe('Just a body'),
    test: z
      .string()
      .refine(() => true)
      .describe('Just a test'),
    another: z
      .string()
      .superRefine(() => true)
      .describe('Just another'),
  })
  .openapi('Test');

const generator = new OpenApiGeneratorV3([schema]);

console.log(JSON.stringify(generator.generateComponents(), null, 4));

Generates the following JSON:

{
    "components": {
        "schemas": {
            "Test": {
                "type": "object",
                "properties": {
                    "type": {
                        "type": "string",
                        "description": "Just a type"
                    },
                    "title": {
                        "type": "string",
                        "description": "Just a title"
                    },
                    "body": {
                        "type": "string",
                        "default": "",
                        "description": "Just a body"
                    },
                    "test": {
                        "type": "string",
                        "description": "Just a test"
                    },
                    "another": {
                        "type": "string",
                        "description": "Just another"
                    }
                },
                "required": [
                    "type",
                    "test",
                    "another"
                ]
            }
        },
        "parameters": {}
    }
}

The zod version is 3.22.4 (I also just tried with the latest - 3.23.4). It is possible that the zod versions are causing this problem. Can you please confirm that this is the test and expected behavior? And if it is can you try and troubleshoot where the problems comes from within your setup.

kernwig commented 2 months ago

@AGalabov Thank you and I'm very sorry for wasting your time. Based on this evidence, I tried the problem down to "deep strict" function https://gist.github.com/jaens/7e15ae1984bb338c86eb5e452dee3010