Skip to content

Commit 4950e8b

Browse files
feat: add hono-openapi-ui middleware
1 parent 4447e48 commit 4950e8b

File tree

21 files changed

+550
-30
lines changed

21 files changed

+550
-30
lines changed

examples/express-openapi-ui/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
},
1010
"scripts": {
1111
"build": "tsc --p tsconfig.json && tsc-alias -p tsconfig.json",
12-
"dev": "nodemon --exec \"vite-node src/index.ts\" --ext ts --quiet --watch ../../packages/express-api-reference --watch ./"
12+
"dev": "nodemon --exec \"vite-node src/index.ts\" --ext ts --quiet --watch ../../packages/express-openapi-ui --watch ./"
1313
},
1414
"dependencies": {
1515
"@openapi-ui/express-openapi-ui": "workspace:*",
@@ -21,7 +21,7 @@
2121
"@types/swagger-jsdoc": "^6.0.3",
2222
"nodemon": "^3.0.1",
2323
"tsc-alias": "^1.8.8",
24-
"vite": "^5.2.10",
24+
"vite": "^5.2.11",
2525
"vite-node": "^1.3.1"
2626
}
2727
}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
{
22
"extends": "../../tsconfig.json",
3-
"include": ["src"],
4-
"exclude": ["dist", "test", "vite.config.ts", "**/*.test.ts"],
53
"compilerOptions": {
64
"types": ["vite/client"],
75
"paths": {
86
"@/*": ["./src/*"]
97
},
108
"outDir": "dist/"
11-
}
9+
},
10+
"include": ["src"],
11+
"exclude": ["dist", "test", "vite.config.ts", "**/*.test.ts"]
1212
}

examples/hono-openapi-ui/package.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "@openapi-ui-examples/hono-openapi-ui",
3+
"type": "module",
4+
"version": "0.1.0",
5+
"author": "rookie-luochao",
6+
"private": true,
7+
"engines": {
8+
"node": ">=18"
9+
},
10+
"scripts": {
11+
"build": "tsc --p tsconfig.json && tsc-alias -p tsconfig.json",
12+
"dev": "nodemon --exec \"vite-node src/index.ts\" --ext ts --quiet --watch ../../packages/hono-openapi-ui --watch ./"
13+
},
14+
"dependencies": {
15+
"@hono/node-server": "^1.11.0",
16+
"@hono/zod-openapi": "^0.8.6",
17+
"@openapi-ui/hono-openapi-ui": "workspace:*",
18+
"hono": "^4.3.8"
19+
},
20+
"devDependencies": {
21+
"nodemon": "^3.0.1",
22+
"tsc-alias": "^1.8.8",
23+
"vite": "^5.2.11",
24+
"vite-node": "^1.3.1"
25+
}
26+
}

examples/hono-openapi-ui/src/index.ts

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
import { serve } from "@hono/node-server";
2+
import { OpenAPIHono, createRoute, z } from "@hono/zod-openapi";
3+
import { openApiUIReference } from "@openapi-ui/hono-openapi-ui";
4+
5+
const app = new OpenAPIHono();
6+
7+
// Example route
8+
app.openapi(
9+
createRoute({
10+
method: "get",
11+
path: "/hello-world",
12+
description: "Respond a message",
13+
tags: ["basic example"],
14+
responses: {
15+
200: {
16+
description: "OK",
17+
content: {
18+
"application/json": {
19+
schema: z.object({
20+
message: z.string(),
21+
}),
22+
},
23+
},
24+
},
25+
},
26+
}),
27+
(c) => {
28+
return c.json({
29+
message: "hello",
30+
});
31+
},
32+
);
33+
34+
app.openapi(
35+
createRoute({
36+
name: "Get all posts",
37+
method: "get",
38+
path: "/posts",
39+
description: "Returns all posts",
40+
tags: ["posts"],
41+
responses: {
42+
200: {
43+
description: "OK",
44+
content: {
45+
"application/json": {
46+
schema: z.object({
47+
posts: z.array(
48+
z.object({
49+
id: z.number().default(123),
50+
title: z.string(),
51+
body: z.string(),
52+
}),
53+
),
54+
}),
55+
},
56+
},
57+
},
58+
},
59+
}),
60+
(c) => {
61+
return c.json({
62+
posts: [
63+
{
64+
id: 123,
65+
title: "My Blog Post Title",
66+
body: "My Blog Post Body",
67+
},
68+
],
69+
});
70+
},
71+
);
72+
73+
app.openapi(
74+
createRoute({
75+
name: "Create post",
76+
method: "post",
77+
path: "/posts",
78+
description: "Create a new post",
79+
tags: ["posts"],
80+
request: {
81+
body: {
82+
content: {
83+
"application/json": {
84+
schema: z.object({
85+
title: z.string(),
86+
body: z.string(),
87+
}),
88+
},
89+
},
90+
},
91+
},
92+
responses: {
93+
200: {
94+
description: "OK",
95+
content: {
96+
"application/json": {
97+
schema: z.object({
98+
id: z.number().default(123),
99+
title: z.string(),
100+
body: z.string(),
101+
}),
102+
},
103+
},
104+
},
105+
},
106+
}),
107+
(c) => {
108+
return c.json({
109+
id: 123,
110+
title: "My Blog Post Title",
111+
body: "My Blog Post Body",
112+
});
113+
},
114+
);
115+
116+
app.openapi(
117+
createRoute({
118+
name: "Delete Post",
119+
method: "delete",
120+
path: "/posts/{id}",
121+
description: "Delete a post",
122+
tags: ["posts"],
123+
request: {
124+
params: z.object({
125+
id: z.number().default(123),
126+
}),
127+
},
128+
responses: {
129+
200: {
130+
description: "OK",
131+
content: {
132+
"application/json": {
133+
schema: z.object({
134+
status: z.string().default("OK"),
135+
message: z.string().default("Post deleted"),
136+
}),
137+
},
138+
},
139+
},
140+
404: {
141+
description: "Not Found",
142+
content: {
143+
"application/json": {
144+
schema: z.object({
145+
status: z.string().default("ERROR"),
146+
message: z.string().default("Post not found"),
147+
}),
148+
},
149+
},
150+
},
151+
},
152+
}),
153+
(c) => {
154+
return c.json({
155+
status: "OK",
156+
message: "Post deleted",
157+
});
158+
},
159+
);
160+
161+
const docsPath = "/openapi";
162+
const specPath = "/openapi.json";
163+
164+
app.doc(specPath, {
165+
info: {
166+
title: "Example API",
167+
description: "Example API description",
168+
version: "v1",
169+
},
170+
openapi: "3.0.0",
171+
});
172+
173+
app.use(
174+
docsPath,
175+
openApiUIReference({
176+
title: "openapi docs",
177+
description: "openapi docs description",
178+
specPath: specPath,
179+
}),
180+
);
181+
182+
const PORT = Number(process.env.PORT) || 8004;
183+
const HOST = process.env.HOST || "0.0.0.0";
184+
serve(
185+
{
186+
fetch: app.fetch,
187+
port: PORT,
188+
hostname: HOST,
189+
},
190+
() => {
191+
console.log(`✅ Hono Middleware listening on http://127.0.0.1:${PORT}${docsPath}`);
192+
},
193+
);
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"extends": "../../tsconfig.json",
3+
"compilerOptions": {
4+
"types": ["vite/client", "node"],
5+
"paths": {
6+
"@/*": ["./src/*"]
7+
},
8+
"outDir": "dist/"
9+
},
10+
"include": ["src"],
11+
"exclude": ["node_modules", "dist", "test", "vite.config.ts", "**/*.test.ts"]
12+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { defineConfig } from "vite";
2+
3+
export default defineConfig({
4+
resolve: {
5+
alias: [],
6+
},
7+
});

packages/express-openapi-ui/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
"devDependencies": {
4444
"@types/express": "^4.17.21",
4545
"tsup": "^7.2.0",
46-
"vite": "^5.2.10",
46+
"vite": "^5.2.11",
4747
"vitest": "^1.6.0"
4848
},
4949
"peerDependencies": {

packages/express-openapi-ui/src/middleware.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export interface IOpenApiUIOption {
88
cdn?: string;
99
}
1010

11-
export const OpenApiUIReference = (options: IOpenApiUIOption) => {
11+
export const loadConfig = (options: IOpenApiUIOption) => {
1212
const { specPath = "/openapi.json", theme, cdn } = options;
1313
return `
1414
<div id="openapi-ui-container" spec-url="${specPath}" theme="${theme}"></div>
@@ -30,7 +30,7 @@ export function openApiUIReference(options: IOpenApiUIOption) {
3030
<meta name="viewport" content="width=device-width, initial-scale=1" />
3131
</head>
3232
<body>
33-
${OpenApiUIReference(options)}
33+
${loadConfig(options)}
3434
</body>
3535
</html>
3636
`);

packages/express-openapi-ui/test/index.spec.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
import { describe, expect, it } from "vitest";
2-
import { OpenApiUIReference } from "../src";
2+
import { loadConfig } from "../src";
33

4-
describe("OpenApiUIReference", () => {
4+
describe("loadConfig", () => {
55
const url = "https://petstore3.swagger.io/api/v3/openapi.json";
66

77
it("renders the given spec URL", () => {
8-
expect(OpenApiUIReference({ specPath: url }).toString()).toContain(
9-
"https://petstore3.swagger.io/api/v3/openapi.json",
10-
);
8+
expect(loadConfig({ specPath: url }).toString()).toContain("https://petstore3.swagger.io/api/v3/openapi.json");
119
});
1210

1311
it("renders the given spec URL with custom cdn", () => {
1412
expect(
15-
OpenApiUIReference({
13+
loadConfig({
1614
specPath: url,
17-
cdn: "https://unpkg.com/openapi-ui-dist",
15+
cdn: "https://cdn.jsdelivr.net/npm/openapi-ui-dist",
1816
}).toString(),
1917
).toContain("openapi-ui-dist");
2018
});

packages/express-openapi-ui/tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
"compilerOptions": {
44
"types": ["vitest/globals"]
55
},
6-
"include": ["src/**/*.ts", "src/**/*.tsx"]
6+
"include": ["src/**/*.ts"]
77
}

0 commit comments

Comments
 (0)