middleware icon indicating copy to clipboard operation
middleware copied to clipboard

[zod-openapi] MaybePromise response type issue

Open Jayllyz opened this issue 9 months ago • 2 comments

All my routes are broken because of this change #522. Are any changes necessary?

hono : 4.3.7 zod : 3.23.8 zod-openapi : 0.12.1 (working good 0.11.1)

Argument of type '(c: Context<Env, "/health", {}>) => Response & TypedResponse<"OK", 200, "json">' is not assignable to parameter of type 'Handler<Env, "/health", {}, MaybePromise<TypedResponse<never, 200, "json">>>'.
  Type 'Response & TypedResponse<"OK", 200, "json">' is not assignable to type 'MaybePromise<TypedResponse<never, 200, "json">>'.
    Type 'Response & TypedResponse<"OK", 200, "json">' is not assignable to type 'TypedResponse<never, 200, "json">'.
      Types of property 'data' are incompatible.
        Type 'string' is not assignable to type 'never'.ts(2345)
export const healthCheck = createRoute({
 method: 'get',
 path: '/health',
 summary: 'Health check',
 description: 'Health check',
 responses: {
   200: {
     description: 'Successful response',
     content: {
       'application/json': {
         schema: { type: 'string' },
       },
     },
   },
 },
 500: serverErrorSchema,
 tags: ['health'],
});

health.openapi(healthCheck, (c) => {
 return c.json('OK', 200);
});

Jayllyz avatar May 16 '24 08:05 Jayllyz

When multiple response definitions are specified for an API, it seems to result in an error if there are differences in the schema.

ok

const app = new OpenAPIHono()
  .openapi(createRoute({
    method: 'get',
    path: '/users',
    responses: {
      200: {
        description: 'users',
        content: {
          'application/json': {
            schema: z.array(
              z.object({
                id: z.string(),
                name: z.string(),
                age: z.number(),
              })
            ),
          },
        },
      },
      204: {
        description: 'internal error',
        content: {
          'application/json': {
            schema: z.array(
              z.object({
                id: z.string(),
                name: z.string(),
                age: z.number(),
              })
            ),
          },
        },
      },
    },
  }),
  async (c) => {
    const users = await getUsersQueryToDatabase()
    if (users.length === 0) {
      return c.json([], 400)
    }
    return c.json(users)
  })

/* This code is also OK.
      204: {
        description: 'internal error',
      },
*/

ng

const app = new OpenAPIHono()
  .openapi(createRoute({
    method: 'get',
    path: '/users',
    responses: {
      200: {
        description: 'users',
        content: {
          'application/json': {
            schema: z.array(
              z.object({
                id: z.string(),
                name: z.string(),
                age: z.number(),
              })
            ),
          },
        },
      },
      500: {
        description: 'internal error',
        content: {
          'application/json': {
            schema: z.array(
              z.object({
                code: z.number(),
                message: z.string(),
              })
            ),
          },
        },
      },
    },
  }),
  async (c) => {
    const users = await getUsersQueryToDatabase()
    if (users.length === 0) {
      return c.json({ code: 500, message: 'internal error'}, 500)
    }
    return c.json(users)
  })

akineko avatar May 16 '24 15:05 akineko

I fixed, Adding a response without content.

example

const app = new OpenAPIHono()
  .openapi(createRoute({
    method: 'get',
    path: '/users',
    responses: {
      200: {
        description: 'users',
        content: {
          'application/json': {
            schema: z.array(
              z.object({
                id: z.string(),
                name: z.string(),
                age: z.number(),
              })
            ),
          },
        },
      },
      // adding this response
      204: {
        description: 'no content',
      },
      500: {
        description: 'internal server error',
        content: {
          'application/json': {
            schema: z.array(
              z.object({
                id: z.string(),
                name: z.string(),
                age: z.number(),
              })
            ),
          },
        },
      },
    },
  }),
  async (c) => {
    const users = await getUsersQueryToDatabase()
    if (users.length === 0) {
      return c.json([], 400)
    }
    return c.json(users)
  })

But it is a temporary workaround.

akineko avatar May 16 '24 16:05 akineko

Hi @Jayllyz

How about using z.string() instead of { type: 'string' }?

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

const app = new OpenAPIHono()

export const healthCheck = createRoute({
  method: 'get',
  path: '/health',
  summary: 'Health check',
  description: 'Health check',
  responses: {
    200: {
      description: 'Successful response',
      content: {
        'application/json': {
          schema: z.string() // <===
        }
      }
    }
  },
  tags: ['health']
})

app.openapi(healthCheck, (c) => {
  return c.json('foo', 200)
})

export default app

yusukebe avatar May 17 '24 09:05 yusukebe

Hi @yusukebe it worked, thanks!

Jayllyz avatar May 17 '24 14:05 Jayllyz