Skip to main content

INX Interface Interaction

In this tutorial, you will learn how to interact with the IOTA Node Extension (INX) interface via JavaScript (NodeJS) with both simple requests and streaming requests. The extension uses the inx.proto file to generate the required gRPC client to connect to HORNET. gRPC is a modern open source high performance Remote Procedure Call (RPC) framework that can run in any environment.

Prerequisites


Set Up

Prepare development environment

Create a new folder for the tutorial in a location of your choice and navigate to it:

mkdir inx-interaction
cd inx-interaction

Run the Node.js initializer to configure the project:

npm init --yes

Overwrite the package.json file with the following content:

{
'name': 'inx-interaction',
'version': '1.0.0',
'description': '',
'main': 'index.js',
'dependencies':
{
'@grpc/grpc-js': '^1.5.5',
'@koa/router': '^10.1.1',
'koa': '^2.13.4',
'koa-logger': '^3.2.1',
},
'scripts': { 'test': 'echo "Error: no test specified" && exit 1' },
'author': '',
'license': 'Apache-2.0',
}

Install new dependencies:

npm install

Prepare configuration file

Create new file config.js and add the following code:

const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
const PROTO_PATH = './inx.proto';

const INX_ADDRESS = 'localhost:9029';

const protoOptions = {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
};

// Load the `inx.proto` file
const packageDefinition = protoLoader.loadSync(PROTO_PATH, protoOptions);
const INX = grpc.loadPackageDefinition(packageDefinition).inx.INX;

// Instantiate an INX client
const client = new INX(INX_ADDRESS, grpc.credentials.createInsecure());

module.exports = client;

Prepare proto file

Copy the inx-proto file from here into your folder. The inx-proto file contains the definition of all available endpoints and below you will see a few example of both simple requests and streaming requests.


Prepare and run files for simple requests

Create new file ReadBlock.js and add the following code

const client = require('./config.js');

// Some console output will be printed in a different color for better readability
const consoleColor = '\x1b[36m%s\x1b[0m';

async function run() {
// Fetch random tip
const TipsRequest = { count: 1, allowSemiLazy: false };

const tipBlockId = await new Promise((resolve, reject) => {
client.RequestTips(TipsRequest, function (error, answer) {
if (error) {
reject(error);
} else {
console.log(consoleColor, 'Random tip');
console.log(answer, '\n');

const blockId = answer.tips[0].id.toString('hex');
resolve(blockId);
}
});
});

// Fetch block
const blockIdBuff = Buffer.from(tipBlockId.toString(), 'hex');
const blockId = { id: blockIdBuff };

await client.ReadBlock(blockId, function (error, answer) {
if (error) {
console.log(error);
} else {
console.log(consoleColor, 'Block');
console.log(answer, '\n');
}
});

// Fetch block metadata
await client.ReadBlockMetadata(blockId, function (error, answer) {
if (error) {
console.log(error);
} else {
console.log(consoleColor, 'Block Metadata');
console.log(answer, '\n');
}
});
}

run().catch((err) => console.error(err));

Run ReadBlock.js

node ./ReadBlock.js

Create new file ReadNodeConfiguration.js and add the following code

const client = require('./config.js');

// Some console output will be printed in a different color for better readability
const consoleColor = '\x1b[36m%s\x1b[0m';

async function run() {
// Fetch and log node configuration
await client.ReadNodeConfiguration({}, function (error, answer) {
if (error) {
console.log(error);
} else {
console.log(consoleColor, 'Node Configuration');
console.log(answer, '\n');
}
});
}

run().catch((err) => console.error(err));

Run ReadNodeConfiguration.js

node ./ReadNodeConfiguration.js

Create new file ReadNodeStatus.js and add the following code

const client = require('./config.js');

// Some console output will be printed in a different color for better readability
const consoleColor = '\x1b[36m%s\x1b[0m';

async function run() {
// Fetch and log node status
await client.ReadNodeStatus({}, function (error, answer) {
if (error) {
console.log(error);
} else {
console.log(consoleColor, 'Node Status');
console.log(answer, '\n');
}
});
}

run().catch((err) => console.error(err));

Run ReadNodeStatus.js

node ./ReadNodeStatus.js

Prepare and run files for streaming requests

Create new file ListenToBlocks.js and add the following code

const client = require('./config.js');

// Listen to the stream of blocks
// Further Options:
// client.ListenToSolidBlocks();
// client.ListenToReferencedBlocks();

var call = client.ListenToBlocks();
call.on('data', function (answer) {
console.log(answer);
});
call.on('end', function () {
// The server has finished sending
});
call.on('error', function (error) {
// An error has occurred and the stream has been closed.
});
call.on('status', function (status) {
// process status
});

Run ListenToBlocks.js

node ./ListenToBlocks.js

Create new file ListenToLatestMilestones.js and add the following code

const client = require('./config.js');

// Listen to the stream of latest milestones
var call = client.ListenToLatestMilestones();
call.on('data', function (answer) {
console.log(answer);
});
call.on('end', function () {
// The server has finished sending
});
call.on('error', function (error) {
// An error has occurred and the stream has been closed.
});
call.on('status', function (status) {
// process status
});

Run ListenToLatestMilestones.js

node ./ListenToLatestMilestones.js

Create new file ReadUnspentOutputs.js and add the following code

const client = require('./config.js');

// Listen to the stream of latest milestones
var call = client.ReadUnspentOutputs();
call.on('data', function (answer) {
console.log(answer);
});
call.on('end', function () {
// The server has finished sending
});
call.on('error', function (error) {
// An error has occurred and the stream has been closed.
});
call.on('status', function (status) {
// process status
});

Run ReadUnspentOutputs.js

node ./ReadUnspentOutputs.js

Feel free to try out additional endpoints of the interface as defined in the inx-proto file.