hono icon indicating copy to clipboard operation
hono copied to clipboard

Swagger Editor middleware

Open yusukebe opened this issue 1 year ago • 11 comments

What is the feature you are proposing?

Like this:

https://editor.swagger.io/

We may make it without depending external libraries, but I think it should be a 3rd party middleware.

yusukebe avatar Sep 06 '23 01:09 yusukebe

For Stoplight Elements, the code we use looks like:

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

declare type Bindings = {
  ENVIRONMENT: "dev" | "production";
};

declare type Env = {
  Bindings: Bindings;
};

const ParamsSchema = z.object({
  id: z
    .string()
    .min(3)
    .openapi({
      param: {
        name: 'id',
        in: 'path',
      },
      example: '1212121',
    }),
})

const UserSchema = z
  .object({
    id: z.string().openapi({
      example: '123',
    }),
    name: z.string().openapi({
      example: 'John Doe',
    }),
    age: z.number().openapi({
      example: 42,
    }),
  })

const route = createRoute({
  method: 'get',
  path: '/users/{id}',
  request: {
    params: ParamsSchema,
  },
  responses: {
    200: {
      content: {
        'application/json': {
          schema: UserSchema,
        },
      },
      description: 'Retrieve the user',
    },
  },
})

const app = new OpenAPIHono<Env>()

app.use('*', logger())


app.openapi(route, (c) => {
  const { id } = c.req.valid('param')
  console.debug(c.env.ENVIRONMENT)
  return c.jsonT({
    id,
    age: 20,
    name: 'Ultra-man',
  })
})

app.doc('/openapi.json', {
  openapi: '3.0.0',
  info: {
    version: '1.0.0',
    title: 'My API'
  },
  tags: [
    {
      name: 'language',
      description: 'en'
    }
  ]
})

app.get('/docs/', (c) => {
  return c.html(`<!doctype html>
<html lang="en">
  <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
      <title>API Docs</title>
      <meta name="author" content="ai.moda">
      <meta name="title" content="API Docs" />
      <script async src="https://stoplight-elements.ai.moda/web-components.min.js"></script>
      <link rel="stylesheet" href="https://stoplight-elements.ai.moda/styles.min.css">
      </head>
    <body>

    <div style="height: 100vh;">
        <elements-api
            apiDescriptionUrl="/openapi.json"
            router="hash"
            layout="sidebar"
            tryItCredentialsPolicy="include"
            withCredentials="true"
        />
    </div>
  </body>
</html>
`, 200, {
  'Content-Security-Policy': "default-src 'none'; script-src https://stoplight-elements.ai.moda/web-components.min.js 'unsafe-eval'; style-src https://stoplight-elements.ai.moda/styles.min.css 'unsafe-inline'; connect-src 'self'"
})
})

export default app

Manouchehri avatar Sep 06 '23 01:09 Manouchehri

Thanks @Manouchehri

Or, I think we can also make "swagger-ui" - @hono/swagger-ui middleware using swagger-ui-dist via CDN:

https://swagger.io/docs/open-source-tools/swagger-ui/usage/installation/

yusukebe avatar Sep 08 '23 07:09 yusukebe

Is there anyone make it?

cc: @sor4chi

yusukebe avatar Sep 08 '23 07:09 yusukebe

I think it is almost the same as the way with the stoplight element. Here is an example using swagger-ui-dist!

import type { Context } from 'hono'

export function swaggerUI() {
  return async (c: Context) =>
    c.html(`
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Swagger UI</title>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/swagger-ui-bundle.min.js"></script>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/swagger-ui.min.css" rel="stylesheet">
  </head>
  <body>
    <div id="swagger-ui"></div>
    <script>
      window.onload = function() {
        SwaggerUIBundle({
          url: '/openapi.json',
          dom_id: '#swagger-ui',
          presets: [
            SwaggerUIBundle.presets.apis,
            SwaggerUIBundle.SwaggerUIStandalonePreset
          ],
        })
      }
    </script>
  </body>
</html>
`)
}

sor4chi avatar Sep 08 '23 11:09 sor4chi

~~I'm trying to see if I can't take advantage of being a thirdparty and pass the return value of createRoute directly to the swaggerUI Middleware option.~~

sor4chi avatar Sep 08 '23 11:09 sor4chi

I also updated https://github.com/honojs/hono/issues/1415#issuecomment-1707531030 to include the correct and "best" CORS rule (that I know of right now) for Stoplight Elements. 😄

Manouchehri avatar Sep 08 '23 14:09 Manouchehri

@yusukebe @Manouchehri should this be an optional config on @hono/zod-openapi?

rafaell-lycan avatar Oct 01 '23 01:10 rafaell-lycan

@rafaell-lycan

No. I think it should be a middleware independent of @hono/zod-openapi like https://github.com/honojs/middleware/pull/168

yusukebe avatar Oct 01 '23 20:10 yusukebe

I think we can close this issue by this Swagger UI Milldeware releases.

@yusukebe

sor4chi avatar Nov 08 '23 08:11 sor4chi

Swagger UI Middleware is published, but Swagger UI is not Swagger Editor: https://github.com/swagger-api/swagger-editor

yusukebe avatar Nov 08 '23 12:11 yusukebe

Opps, I see.

sor4chi avatar Nov 08 '23 13:11 sor4chi