Azure Node Functions Apps and OpenAPI Swagger

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.

Links

Paul Sterl has written 55 articles

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>