Reusability causing problems

Jonas Lagoni Avatar

Jonas Lagoni

ยท3 min read

When you enforce reusability, using such a sharded document can be rather problematic at times in tooling. I encountered this problem when I wanted to render the AsyncAPI files within the developer platform for GamingAPI.

The way I solved the problem was to provide a bundled AsyncAPI document instead of multiple small files.

A sharded document

There are many ways to utilize references, but take this document for example, that uses a local reference to define the message.

1{
2  "asyncapi": "2.3.0",
3  "info": {
4    "title": "Some API",
5    "version": "0.0.0"
6  },
7  "channels": {
8    "some/channel": {
9      "subscribe": {
10        "message": {
11          "$ref": "./components/messages/SomeMessage.json"
12        }
13      }
14    }
15  }
16}

./asyncapi.json

1{
2  "name": "SomeMessage",
3  "payload": {
4    "type": "object"
5  }
6}

./components/messages/SomeMessage.json

The bundled AsyncAPI document would then have no, or only local references within the same document.

1{
2  "asyncapi": "2.3.0",
3  "info": {
4    "title": "Some API",
5    "version": "0.0.0"
6  },
7  "channels": {
8    "some/channel": {
9      "subscribe": {
10        "operationId": "ServerStarted",
11        "message": {
12          "name": "SomeMessage",
13          "payload": {
14            "type": "object"
15          }
16        }
17      }
18    }
19  }
20}

./asyncapi.bundled.json

Easier to consume

The problem I faced was occurring because the local references have to be resolved in tooling. While it is, and should, be possible to use the sharded AsyncAPI documents with external references, I just find it easier to work and share them as bundled documents.

The only downside about the bundled documents is that you cannot, or rather should not, make any changes to them. This means they can only be used as read-only, which might or might not affect whether you use this.

To achieve the bundling I am using the official AsyncAPI bundler which is maintained and created by Souvik.

To bundle the documents we can create a simple code script (at least until the CLI has this feature) which can bundle all the documents we want:

1// Because the bundler does not work outside of the working directory we have to keep this package here for now: https://github.com/asyncapi/bundler/issues/35
2const bundler = require('@asyncapi/bundler');
3const fs = require('fs');
4const path = require('path');
5
6async function bundleDocuments(filePath, outputFile) {
7  const fullPath = path.resolve(filePath);
8  const content = fs.readFileSync(fullPath, 'utf-8');
9  const fileContent = JSON.parse(content);
10  const bundledDocument = await bundler(
11    [fileContent]
12  );
13  fs.writeFileSync(outputFile, bundledDocument.string());
14};
15bundleDocuments('./rust_server.asyncapi.json', '../bundled/rust_server.asyncapi.bundled.json');

Because of #35, the code MUST be within the same folder as the AsyncAPI documents, but that should only be temporary.

As introduced in Enforcing consistency guidelines, running the linter was done through a simple package.json file, we can now adapt this file to also include a script for bundling documents:

1{
2  "scripts": {
3    ...,
4    "bundle": "cd documents && node bundle-documents.js"
5  },
6  "dependencies": {
7    ...,
8    "@asyncapi/bundler": "0.1.0"
9  }
10}

Adapting release workflow

The release workflow for the AsyncAPI documents can then be adapted to always run this script when the API version for the application changes:

1...
2jobs:
3  bump:
4    steps:
5      ...
6      - if: steps.version_bump.outputs.wasBumped == 'true'
7        name: Bundle documents
8        run: npm run bundle
9      ...

Photo by Patrick Tomasso on Unsplash