26
Nov 2017
Using Swagger UI with any codebase
Tags |
On Computer Technology
This post shows you how to use Swagger UI with any codebase. There are no library dependencies once you extract the assets. The only things you need are a web server and a file containing the API documentation in the OpenAPI format.
At the end of the post, you should be able to use your web server to host API documentation that looks something like http://petstore.swagger.io
Like many of my posts, this is technical and there are instructions you have to follow.
The GitHub repo is at https://github.com/yanhan/swagger-ui-any-codebase
We will be making use of some files there in this demo.
We will be making use of the swagger-ui-dist npm package and extracting the assets from there. To do so, we will be using nvm to install npm and use npm to install swagger-ui-dist. Once that is done, we can extract the assets.
With the assets and an API documentation file, we can use a web server to serve the API documentation Swagger UI style.
Create a new directory somewhere to reduce clutter. We will be doing everything in this new directory.
This section focuses on extracting the static assets required by Swagger UI.
Follow the instructions at the nvm GitHub README. I have copied and pasted what I used:
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.6/install.sh | bash
nvm allows us to install multiple versions of Nodejs without having to override the system’s Nodejs.
I am using Nodejs 8.9.1 in this demo. I believe any 8.x Nodejs should work.
nvm install 8.9.1
Create a package.json
file with the following contents:
{
"version": "0.1.0",
"dependencies": {
"swagger-ui-dist": "~3.5.0"
}
}
Then activate Nodejs 8.9.1 and install the package:
nvm use 8.9.1
npm install
You should see a node_modules
directory. We will be extracting the static assets of swagger-ui-dist
from it soon.
Copy the node_modules/swagger-ui-dist
directory:
cp -R node_modules/swagger-ui-dist .
In the swagger-ui-dist
directory, there is a file named index.html
. Use a text editor to open that file and go to line 76. You should see code like this:
const ui = SwaggerUIBundle({
url: "http://petstore.swagger.io/v2/swagger.json",
For purposes of the demo below, modify the value of the url
key so that it looks like:
const ui = SwaggerUIBundle({
url: "/api-docs.yml",
This is telling the code to fire a request to /api-docs.yml
to load the API documentation. Feel free to change the name of this endpoint, as long as you configure your web server supports it and return the correct file.
In fact, you can copy / move the entire swagger-ui-dist
directory to your web server’s repository and take things from there (while skipping the rest of this article). If you are unsure on how to proceed, carry on reading so you can see a demo of how we incorporate some example API documentation so it is hosted on a Flask server.
This is the easy way.
Simply clone https://github.com/yanhan/swagger-ui-any-codebase and run the following commands to build the Docker image and run it:
./build-docker.sh
./run-docker.sh
Then go to http://127.0.0.1:5000/swagger-ui
. Tadah!
This is the hard way. With the Docker option, I don’t really recommend doing this unless you want to familiarize yourself with the pyenv toolchain.
Follow the instructions at the pyenv GitHub README to install pyenv. I used the instructions at the pyenv-installer to install pyenv. Specifically, the following command:
curl -L https://raw.githubusercontent.com/pyenv/pyenv-installer/master/bin/pyenv-installer | bash
Now we install Python 3.6.3 using pyenv. Any version of Python 3.x should work fine.
pyenv install 3.6.3
If you encounter any errors installing pyenv, google is your friend.
We will be creating a virtualenv to install Flask. We will be naming it swagger-demo
.
pyenv virtualenv 3.6.3 swagger-demo
Now we will be installing Flask inside the swagger-demo
virtualenv:
pyenv activate swagger-demo
pip install -r requirements.txt
Get it from https://github.com/yanhan/swagger-ui-any-codebase/blob/master/server.py and save it as server.py
.
We will be going through some of the code later.
Get it from https://github.com/yanhan/swagger-ui-any-codebase/blob/master/api-docs.yml and save it as api-docs.yml
.
This is our example API documentation saved as a YAML file.
python server.py
Then go to http://127.0.0.1:5000/swagger-ui to see the documentation. Tadah!
Understand this section and you can adapt the Swagger UI assets to any codebase you are using. You are recommended to open https://github.com/yanhan/swagger-ui-any-codebase/blob/master/server.py and refer to it as we explain the code.
Recall that earlier we extracted the swagger-ui-dist
directory from the swagger-ui-dist NPM package. That directory contains all the static assets required. We just need to find a way for the server to deliver the swagger-ui-dist/index.html
file on a user request.
Notice that to see the docs, we go to the path /swagger-ui
, which serves the swagger-ui-dist/index.html
file. Which implies that our web server must have an endpoint for this path taht serves the file. For our demo, it is right here:
@app.route("/swagger-ui/")
def swagger_ui():
return send_from_directory(SWAGGER_UI_DIST_DIR, "index.html")
This tells Flask to return the index.html
page from our swagger-ui-dist
directory. Recall that earlier, we modified that file so the SwaggerUIBundle
has its url
value changed:
const ui = SwaggerUIBundle({
url: "/api-docs.yml",
which implies we must have another route to /api-docs.yml
that returns the file containing the API documentation. Indeed, it is here:
@app.route("/api-docs.yml")
@nocache
def swagger_api_docs_yml():
return send_from_directory(".", "api-docs.yml")
The endpoint and the filename do not have to be the same as long as the endpoint you are using returns the correct file; you can use whatever name you like for the endpoint and the filename. We are only using the same name to reduce confusion. I added the nocache
decorator so that the docs served by the Flask server will be updated as I save the actual file and refresh my browser. Credits to Aru Sahni’s blog post for this code.
Finally, if we open swagger-ui-dist/index.html
, we will see some references to other static assets with the ./
prefix, for instance:
./swagger-ui.css
./favicon-32x32.png
./favicon-16x16.png
These are all available at the swagger-ui-dist
directory. Due to the ./
and the fact that we are hosting the API docs at /swagger-ui
, these assets will be requested from the web server under /swagger-ui
. For instance, /swagger-ui/favicon-32x32.png
.
Hence we need an endpoint for serving these assets, right here:
@app.route("/swagger-ui/<asset>")
def swagger_assets(asset):
return send_from_directory(SWAGGER_UI_DIST_DIR, asset)
If we do not do this step, we will be able to load /swagger-ui
but fail to load the other static assets. Open the Network Inspector / similar of your web browser and you will see a number of 404s. In fact, that was how I debugged the problem.
The big drawback to this approach is security. Specifically, exposure of internal API docs to the public Internet. Here, I highlight some concerns that I see.
And that completes our explanation of the web server code. Hopefully, you will be able to adapt this to any codebase.
Disclaimer: Opinions expressed on this blog are solely my own and do not express the views or opinions of my employer(s), past or present.