Skip to content

Commit 12cade2

Browse files
[Docs] Add a tutorial for adding /openapi and /openapi.yaml to a server (#429)
### Motivation In addition to the example project, also document how to add the /openapi and /openapi.yaml endpoints to an existing server. ### Modifications Added the tutorial. ### Result Developers can vend their OpenAPI documents and rendered docs in a consistent way. ### Test Plan Walked through it manually to verify that the steps lead to a working example. --------- Co-authored-by: Si Beaumont <[email protected]>
1 parent 15f7f45 commit 12cade2

25 files changed

+310
-23
lines changed

Sources/swift-openapi-generator/Documentation.docc/Swift-OpenAPI-Generator.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ The generated code, runtime library, and transports are supported on more platfo
7777

7878
### OpenAPI
7979
- <doc:ExploreOpenAPI>
80+
- <doc:Adding-openapi-and-swagger-ui-endpoints>
8081
- <doc:Practicing-spec-driven-API-development>
8182
- <doc:Useful-OpenAPI-patterns>
8283
- <doc:Supported-OpenAPI-features>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
@Tutorial(time: 5) {
2+
@Intro(title: "Adding OpenAPI and Swagger UI endpoints") {
3+
One of the most popular ways to share your OpenAPI document with your users is to host it alongside your API server itself.
4+
5+
Typically this is at `/openapi.yaml` or similar, which serves a plain-text OpenAPI document for consumpion by clients.
6+
7+
Additionally, you can host an HTML page that renders the OpenAPI document as interactive documentation that you can use from the browser, for example using [swagger-ui](https://github.com/swagger-api/swagger-ui).
8+
9+
In this tutorial we'll add both endpoints to our Vapor server.
10+
11+
> Tip: The [OpenAPIEndpointsServer example package](https://github.com/apple/swift-openapi-generator/tree/main/Examples/OpenAPIEndpointsServer) contains the configured Vapor server, in case you want to see the result of this tutorial.
12+
}
13+
14+
@Section(title: "Add an /openapi.yaml endpoint") {
15+
16+
We'll start with the server in its state following <doc:ServerSwiftPM> (before the `/emoji` endpoint is added) and we'll add an `/openapi.yaml` endpoint that serves the OpenAPI document as a static resource.
17+
18+
@Steps {
19+
@Step {
20+
Create a `Public/` directory for serving static content.
21+
22+
@Code(name: "console", file: server-openapi-endpoints.console.0.txt, reset: true)
23+
}
24+
@Step {
25+
Move `openapi.yaml` from `Sources/` and create a symlink to it back in the `Sources/` directory.
26+
27+
@Code(name: "console", file: server-openapi-endpoints.console.1.txt, previousFile: server-openapi-endpoints.console.0.txt)
28+
}
29+
@Step {
30+
In `main.swift`, Add a Vapor middleware that serves the contents of the `Public/` directory.
31+
32+
@Code(name: "main.swift", file: server-openapi-endpoints.main.1.swift, previousFile: server-openapi-endpoints.main.0.swift)
33+
}
34+
@Step {
35+
From the `Product` menu, select `Scheme > Edit Scheme...`. With `Run` selected in the sidebar, select the `Options` tab and check the box labeled `Use custom working directory` and use the path containing Package.swift for this package.
36+
37+
This step is necessary because the Vapor middleware serves files relative to the current working directory for the running server process.
38+
}
39+
@Step {
40+
Test this enpoint in your browser, or using curl.
41+
42+
@Code(name: "console", file: server-openapi-endpoints.console.2.txt, previousFile: server-openapi-endpoints.console.1.txt)
43+
}
44+
}
45+
}
46+
@Section(title: "Add a Swagger UI endpoint") {
47+
Now we'll add a static `openapi.html` page that serves Swagger UI and add a redirect to this page from `/` for discoverability.
48+
49+
@Steps {
50+
@Step {
51+
Create the file `Public/openapi.html` with the swagger-ui JavaScript.
52+
53+
By placing it in the public directory, it is already reachable at `/openapi.html`.
54+
55+
@Code(name: "openapi.html", file: server-openapi-endpoints.openapi.html, reset: true)
56+
}
57+
@Step {
58+
Add a redirect for `/openapi` to `openapi.html`, which serves the rendered documentation.
59+
60+
@Code(name: "main.swift", file: server-openapi-endpoints.main.2.swift, previousFile: server-openapi-endpoints.main.1.swift)
61+
}
62+
@Step {
63+
Add a relative server URL to the OpenAPI document.
64+
65+
This allows you to use the rendered documentation to make requests to the instance serving the OpenAPI document itself.
66+
67+
@Code(name: "main.swift", file: server-openapi-endpoints.openapi.1.yaml, previousFile: server-openapi-endpoints.openapi.0.yaml)
68+
}
69+
@Step {
70+
Visit `http://localhost:8080/openapi` in your browser which should show the rendered documentation for the OpenAPI document.
71+
}
72+
}
73+
}
74+
}

Sources/swift-openapi-generator/Documentation.docc/Tutorials/ServerSwiftPM.tutorial

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
@Code(name: "Package.swift", file: server.Package.2.swift)
6161
}
6262
@Step {
63-
Now we can update our target to make use of the Swift OpenAPI Generator plugin and add the runtime dependencies for our target.
63+
Now we can update our target to make use of the Swift OpenAPI Generator plugin.
6464
@Code(name: "Package.swift", file: server.Package.3.swift)
6565
}
6666
@Step {

Sources/swift-openapi-generator/Documentation.docc/Tutorials/Swift OpenAPI Generator.tutorial

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,6 @@
3030
@TutorialReference(tutorial: "doc:ServerSwiftPM")
3131
@TutorialReference(tutorial: "doc:ClientSwiftPM")
3232
@TutorialReference(tutorial: "doc:ClientXcode")
33+
@TutorialReference(tutorial: "doc:Adding-openapi-and-swagger-ui-endpoints")
3334
}
3435
}

Sources/swift-openapi-generator/Documentation.docc/Tutorials/_Resources/client.Package.0.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ let package = Package(
55
name: "GreetingServiceClient",
66
targets: [
77
.executableTarget(
8-
name: "GreetingServiceClient",
9-
path: "Sources"
8+
name: "GreetingServiceClient"
109
)
1110
]
1211
)

Sources/swift-openapi-generator/Documentation.docc/Tutorials/_Resources/client.Package.1.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ let package = Package(
1111
],
1212
targets: [
1313
.executableTarget(
14-
name: "GreetingServiceClient",
15-
path: "Sources"
14+
name: "GreetingServiceClient"
1615
)
1716
]
1817
)

Sources/swift-openapi-generator/Documentation.docc/Tutorials/_Resources/client.Package.2.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ let package = Package(
1616
],
1717
targets: [
1818
.executableTarget(
19-
name: "GreetingServiceClient",
20-
path: "Sources"
19+
name: "GreetingServiceClient"
2120
)
2221
]
2322
)

Sources/swift-openapi-generator/Documentation.docc/Tutorials/_Resources/client.Package.3.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ let package = Package(
1717
targets: [
1818
.executableTarget(
1919
name: "GreetingServiceClient",
20-
path: "Sources",
2120
plugins: [
2221
.plugin(
2322
name: "OpenAPIGenerator",

Sources/swift-openapi-generator/Documentation.docc/Tutorials/_Resources/client.Package.4.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ let package = Package(
2727
package: "swift-openapi-urlsession"
2828
),
2929
],
30-
path: "Sources",
3130
plugins: [
3231
.plugin(
3332
name: "OpenAPIGenerator",

Sources/swift-openapi-generator/Documentation.docc/Tutorials/_Resources/client.Package.5.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ let package = Package(
2727
package: "swift-openapi-urlsession"
2828
),
2929
],
30-
path: "Sources",
3130
plugins: [
3231
.plugin(
3332
name: "OpenAPIGenerator",

0 commit comments

Comments
 (0)