API Builder is a tool that enables developers to rapidly create APIs and microservices with a low-code/no-code approach. API Builder achieves this by enabling you to develop your business logic using Flows. A Flow allows you to orchestrate your business logic visually as a graph of connected flow-nodes.
The Axway Flow SDK (axway-flow-sdk
) is a standalone utility that enables the creation of custom flow-nodes for API Builder flows. By offering the Axway Flow SDK as a standalone utility, new flow-nodes can be developed and consumed in API Builder.
In this post, we'll look at features available in the Axway Flow SDK and how easy it is to create a new flow-node. For this example, we'll create an MD5 flow-node and integrate it into an API Builder service.
npm install -g axway-flow-sdk
The following generates a new flow-node starter project in the current directory. You can customize the starter project to meet your requirements.
axway-flow -n <node name>
cd <node name>
npm install
npm run build
The generated starter project name is prefixed with the required api-builder-plugin-fn-
prefix.
The starter project is heavily commented to simplify the process of customizing it. It also comes with the eslint configuration and the mocha unit tests incorporated to help you ensure the quality of your custom flow-node.
As an example of how to write a flow-node, we will examine creating a flow-node that allows the hashing of strings from within the flow.
axway-flow -n md5 -d 'MD5 hashing of strings.'
cd api-builder-plugin-fn-md5
npm install
npm install md5
npm run build
const sdk = require('axway-flow-sdk');
const action = require('./action');
function getFlowNodes() {
const flownodes = sdk.init(module);
// The unique name of your flow-node. You can define multiple flow-nodes in this
// file, but one is typical.
flownodes.add('md5', {
name: 'Md5',
// file support for: svg, png, gif, bmp, jpg, and tiff
icon: 'icon.svg',
description: 'MD5 hashing of strings.'
})
// Add a method to your flow-node.
.method('md5', {
name: 'MD5',
description: 'Perform a MD5 hash.'
})
// Add parameter(s) to your method.
.parameter('plaintext', {
description: 'The plain text to hash.',
type: 'string'
}, true)
// Once all parameters for the method are defined, add output(s) to your method.
.output('next', {
name: 'Next',
description: 'The hashed value',
context: '$.hash',
schema: {
type: 'string'
}
})
.action(action);
return Promise.resolve(flownodes);
}
exports = module.exports = getFlowNodes;
To explain what occurs in the index.js file, we will break the file down piece by piece.
.add('md5', {
name: 'Md5',
// file support for: svg, png, gif, bmp, jpg, and tiff
icon: 'icon.svg',
description: 'MD5 hashing of strings.'
})
The name is the text that is displayed in the Flow Editor. The default icon is a placeholder (a star) that should be replaced with a graphic that represents the action of the flow-node. The icon is displayed at 28 pixels x 28 pixels. The category is the section in the Flow Editor tool panel where the flow-node is contained.
To download and use an icon that represents a hash, click Hash icon for MD5 flow-node.
.method('md5', {
name: 'MD5',
description: 'Perform a MD5 hash.'
})
A method called md5, that is displayed in the Flow Editor as MD5, was added. The md5 method has a single parameter. If there was more than one parameter, we would repeat the .parameter(name, schema)
block. The second value in the parameter method is a JSON schema that describes the parameter type.
.output('next', {
name: 'Next',
description: 'The hashed value',
context: '$.hash',
schema: {
type: 'string'
}
})
The outputs section defines the possible outcomes of the flow-node. In this simple case there is just one output; however, flow-nodes can have multiple outputs with different return types. For example, this flow-node could have added an error output to indicate that encoding failed.
.action(action);
The action() expects a function that will be passed the request details parameter and a callback object parameter.
To simplify the management of the code, the starter project puts the implementation of the methods in the action.js file. There isn't a requirement to follow this pattern, you can structure your project how best suits your needs.
const md5 = require('md5');
exports = module.exports = function (req, cb) {
const param = req.params.plaintext;
if (!param) {
// invoking the callback with an error will terminate the flow.
return cb('invalid argument');
}
cb.next(null, md5(param));
};
This is a simple scenario, but it highlights the main features. The parameters for the flow-node method are accessed under the req.params parameter (defined in the index.js file). In this example, the parameter for the hash method is defined as plaintext:
.parameter('plaintext', {
description: 'The plain text to hash.',
type: 'string'
}, true)
The logic checks that the parameter is set. If plaintext is not set, it fires a generic error callback.
return cb('invalid argument');
The starter project includes automatically generated unit tests in the ./test
directory of your project. The tests are for example purposes and will need to be customized to your specific use case. The SDK provides a mocknode that allows for mock invocations of your flow-node methods.
This example uses mocha to check that the specification is defined well enough to pass the plaintext argument to the method. It also mocks the callback using the defined output of the specification and ensures that the method invokes the correct callback.
const md5 = require('md5');
it('[TEST-3] should succeed', () => {
const hashed = md5('some string');
return mocknode(flownodes).node('md5')
.invoke('md5', { plaintext: 'some string' })
.then((data) => {
expect(data).to.deep.equal({
next: [ null, hashed ]
});
});
});
This example is similar to the previous example, except that the method will invoke a cb('invalid argument')
when given an undefined parameter.
it('[TEST-4] should fail to with invalid argument', () => {
return mocknode(flownodes).node('md5')
.invoke('md5', { plaintext: null })
.then((data) => {
expect(data).to.deep.equal(
[ 'invalid argument' ]
);
});
});
The Axway Flow SDK tries to prevent the creation of invalid flow-node specifications, but there are some edge cases where it may be possible to generate a flow-node specification that is invalid at runtime. To detect this, the generated specification should be validated as part of your unit tests.
it('[TEST-5] should define valid node specs', () => {
expect(validate(flownodes)).to.not.throw;
});
While unit testing is important, it is also necessary to be able to install the custom flow-node into your local API Builder service for testing.
This can be achieved by packing the module locally:
cd api-builder-plugin-fn-md5
npm install
npm run build
npm pack
This will create a TGZ archive (api-builder-plugin-fn-md5-1.0.0.tgz) that can then be installed into your Arrow application.
npx @axway/api-builder init md5-demo
cd md5-demo
npm install --no-optional
npm install <path to flow node project>/api-builder-plugin-fn-md5-1.0.0.tgz
npm start
API Builder will now be running and accessible on http://localhost:8080/console.
Navigate to the Greeting API endpoint.
Click on Flow link to navigate to the Flow Editor.
The following displays the completed state of our flow.
The sidebar lists the newly created MD5 flow-node under General. Drag and drop it in the editor and place it between the Check Username flow-node and the Format Greeting flow-node.
In the MD5 flow-node, we have to make one change under parameters.
Use this snippet:
$.params.username
In the Format Greeting flow-node, we have to change the template so we can see our hashed string.
Use this snippet:
{{=it.config.helloworld.salutation}}
{{=it.hash}}
That's all the changes we need to implement our new flow-node. We can now apply the changes and close the editor. Closing the editor will return us to the Greeting API endpoint. In order to test endpoint, we have to expand the endpoint and enter a random username and click Execute. The execute response should appear as follows:
Creating with Axway Flow SDK is a simple, fast, and easy process. You can create various flow-nodes and unit test them before using them in API Builder service endpoints. This gives you the flexibility to integrate with the endpoints you need to, without having to sacrifice the low-code/no-code experience that API Builder provides.