Sometimes we face the problem that we want to host a very simple web-service including a swagger-ui / OpenApi-UI. The question now is, what is the simplest solution, which works everywhere, even in a azure function app?
Where are of course some libs etc. which on the other hand require us to run the web-server. If we look into the doc using the simple unpkg HTML files looks like to be the simplest solution.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="description" content="SwaggerUI" /> <title>SwaggerUI</title> <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@4.5.0/swagger-ui.css" /> </head> <body> <div id="swagger-ui"></div> <script src="https://unpkg.com/swagger-ui-dist@4.5.0/swagger-ui-bundle.js" crossorigin></script> <script> window.onload = () => { window.ui = SwaggerUIBundle({ url: 'https://petstore3.swagger.io/api/v3/openapi.json', dom_id: '#swagger-ui', }); }; </script> </body> </html>
What needs to be done
- Get the HTML OpenAPI HTML file
- Provide an OpenAPI YML or JSON
- Create an endpoint which should host the doc
This solution works also for a spring web app or a AWS function. We will go ahead and look into a Node JavaScript example to see how it may look like:
Which means we have to create a new azure function endpoint where we want to host the OpenApi-UI:
function.json
We can set the function to „anonymous“ if we like and only get is required:
{ "bindings": [ { "authLevel": "anonymous", "type": "httpTrigger", "direction": "in", "name": "req", "methods": [ "get" ] }, { "type": "http", "direction": "out", "name": "res" } ] }
openApi.html
The only adjustment to the original HTML file is the source of the yml
or json
file. You could also adjust the JS and CSS url:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="description" content="My SwaggerUI" /> <title>SwaggerUI</title> <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@4.5.0/swagger-ui.css" /> </head> <body> <div id="swagger-ui"></div> <script src="https://unpkg.com/swagger-ui-dist@4.5.0/swagger-ui-bundle.js" crossorigin></script> <script> window.onload = () => { window.ui = SwaggerUIBundle({ url: './doc?file=openApi.yml', dom_id: '#swagger-ui', }); }; </script> </body> </html>
openApi.yml
Just as an example here. Note here the URL
which has a funny value of _server.url_
, we will replace this value later.
openapi: 3.0.2 info: title: My OpenAPI version: v1 servers: - url: "_server.url_" paths: /foo: get: summary: Som cool function responses: '200': description: Retrieved entities content: application/json: schema: type: string
index.js
"use strict"; const fs = require('fs'); const util = require('util'); const readFileAsync = util.promisify(fs.readFile); module.exports = async function (context, req) { switch(req.query.file) { case 'openApi.yml': // get the url and remove the doc suffix, azure specific const url = req.headers.referer.replace('/doc', ''); let yml = await readFileAsync('doc/openApi.yml', 'UTF-8'); yml = yml.replace('_server.url_', url); context.res = { body: yml, headers: { "Content-Type": "application/yml; charset=utf-8" } }; break; default: context.res = { body: await readFileAsync('doc/openApi.html', 'UTF-8'), headers: { "Cache-Control": "max-age=600", // optional cache header "Content-Type": "text/html; charset=utf-8" } }; break; } }
The switch case is where to ensure we load only files we want to publish, as so you have to extend the case block if you want to host the JS files too. Of course they have to be added to your directory.