Implement InfluxDB Server with Node.js on Ubuntu 20.04
Introduction
InfluxDB is a high-performance time-series database written in the Go programming language. The open-source database application supports several hundred thousand writes per second. InfluxDB optimization for speed makes it the most suitable database server for storing and monitoring time-stamped metrics. The metrics are useful in IoT, financial, transportation, and telecommunication industries.
Node.js is a cross-platform run-time engine that executes Javascript code. Node.js competes with other modern backend programming languages like PHP, Go, and Python. The Node.js non-blocking model makes it a suitable choice for highly-available applications such as chat applications, real-time collaborating tools, streaming applications, and more.
This guide shows you how to code a Node.js application that interacts with an InfluxDB database server On Ubuntu 20.04. The application writes data points to InfluxDB and queries the database to present the information in an HTML table.
Prerequisites
To follow along with this guide:
Create a non-root
sudo
user and install the following packages:
1. Set Up a Sample Database
In this guide, your sample application stores data permanently on an InfluxDB server. The data may come from IoT devices such as smartwatches, energy meters, bank applications, and more in a production environment. This guide uses sample data from solar customers. The energy consumed by the customers in kilowatt-hours, otherwise known as a KWh, is live-streamed to the application via the HTTP protocol. Then, the application stores the data in the InfluxDB server for further processing.
SSH to your server and execute the following steps to create and initialize a sample InfluxDB database:
Log in to the InfluxDB server. Replace
admin
andEXAMPLE_PASSWORD
with the correct InfluxDB administrator credentials.$ influx -username 'admin' -password 'EXAMPLE_PASSWORD'
Output.
>
Create a sample
solar_customers
database.> CREATE DATABASE solar_customers
Output.
>
Switch to the new
solar_customers
database.> USE solar_customers
Output.
Using database solar_customers
Insert sample data points into the
solar_customers
database. Specifypower_usage
as the metric name. In the InfluxDB model, a data point (For instance,power_usage,customer_id=1 KWh=800
) is like a record, and a metric (For example,power_usage
) is like a table in SQL-based databases.> INSERT power_usage,customer_id=1 KWh=800 > INSERT power_usage,customer_id=2 KWh=1750 > INSERT power_usage,customer_id=3 KWh=2560 > INSERT power_usage,customer_id=4 KWh=652 > INSERT power_usage,customer_id=5 KWh=920
Output.
... >
Query the
power_usage
metric to ensure the data is in place.> SELECT "customer_id", "KWh" FROM "power_usage"
Output.
time customer_id KWh ---- ----------- --- 1662015648115956829 1 800 1662015655675425278 2 1750 1662015662345951963 3 2560 1662015669315582055 4 652 1662015675915505403 5 920
Log out from the InfluxDB server.
> quit
The sample database is now ready to accept new data points. Proceed to the next step, creating a Node.js module for accessing the InfluxDB database.
2. Create a Database Gateway Class
When creating a data-driven application with Node.js, it is conventional to have a separate class that provides database functions to the rest of the Node.js modules. A separate database gateway class allows you to define your database credentials in a single file making troubleshooting straightforward in case the application fails to connect to the database. Follow the steps below to create the class:
Create a
project
directory for the Node.js application.$ mkdir project
Switch to the new
project
directory.$ cd project
Open a new
database_gateway.js
file in a text editor.$ nano influxdb_gateway.js
Enter the following information into the
influxdb_gateway.js
file. ReplaceEXAMPLE_PASSWORD
with the correct password for the InfluxDBadmin
account.class influxdb_gateway { dbConn() { const influxDb = require('influxdb-nodejs'); const dbHost = '127.0.0.1'; const dbPort = '8086'; const dbUser = 'admin'; const dbPass = 'EXAMPLE_PASSWORD'; const dbName = 'solar_customers'; const conString = 'http://' dbUser ':' dbPass '@' dbHost ':' dbPort '/' dbName; const influxDbClient = new influxDb(conString); return influxDbClient; } } module.exports = influxdb_gateway;
Save and close the
database_gateway.js
file.
The influxdb_gateway.js
file explained:
The
influxdb_gateway.js
contains a singleinfluxdb_gateway
class. The class then houses adbConn()
method.class influxdb_gateway { dbConn() { ... } } ...
The
dbConn()
method loads theinfluxdb-nodejs
module. This module provides InfluxDB functionalities to the Node.js application. Then, thedbConn()
method connects to the InfluxDB server and returns the connection using theconst influxDbClient = new influxDb(conString);
andreturn influxDbClient;
statements.The
module.exports = influxdb_gateway;
at the end of the file allows you to import and use theinfluxdb_gateway
module in other Node.js source code files using the following declarations.const influxdb_gateway = require('./influxdb_gateway'); const dg = new influxdb_gateway(); const dbConn = dg.dbConn();
After coding the database gateway class, the next step involves creating a module that manages the InfluxDB power_usage
metric.
3. Create the Database Metric class
This application measures a single power_usage
metric for demonstration purposes. Complex applications may contain dozens or hundreds of metrics. Because each metric is different, you should code its functionalities in a separate file. Follow the steps below to create a power_usage
metric class:
Open a new
power_usage.js
file in a text editor.$ nano power_usage.js
Enter the following information into the
power_usage.js
file.class power_usage { constructor(dbConn) { this.dbConn = dbConn; this.measurement = 'power_usage'; } readPoints(callBack) { const reader = this.dbConn.query(this.measurement); reader.then(function (data) { const dataPoints = data.results[0].series[0].values; const dataColumns = data.results[0].series[0].columns; callBack(dataPoints, dataColumns) }).catch( console.error ); } writePoint(pointData, callBack) { this.dbConn.write(this.measurement).tag({ customer_id: pointData["customer_id"] }).field({ KWh: pointData["KWh"] }).then( callBack("Success.") ).catch( console.error ); } } module.exports = power_usage;
Save and close the
power_usage.js
file.
The power_usage.js
file explained:
The
power_usage.js
file contains onepower_usage
class and three methods.class power_usage { constructor(dbConn) { ... } readPoints(callBack) { ... } writePoint(pointData, callBack) { ... } }
The methods in the
power_usage
class perform the following functions:constructor(dbConn)
: This method initializes thepower_usage
class. The constructor method accepts adbConn
argument containing a connection to the InfluxDB server. Thethis.measurement = 'power_usage';
statement sets the name of the measurement. When initializing thepower_usage
class, you must pass an InfluxDB server connection through the constructor function per the following illustration.const power_usage = require('./power_usage'); const pu = new power_usage(dbConn);
readPoints(callBack)
: This method runs thethis.dbConn.query(this.measurement);
function that queries and retrieves data points from the InfluxDB server. ThecallBack
argument accepts a function that runs when the method completes executing. After thereadPoints(callBack)
method runs, it returns the data points and data columns to the calling function using thecallBack(dataPoints, dataColumns)
statement.writePoint(pointData, callBack)
: This method writes a new measurement data point to the InfluxDB server by running thethis.dbConn.write(this.measurement).tag({...}).field({...})
function. ThewritePoint(...)
function accepts thepointData
in JSON format and thecallBack
function that runs when the method completes.
The
module.exports = power_usage;
declaration at the end of thepower_usage
class avails the module to other source code files. Using the following code block, you may initialize and use thepower_usage
module.const power_usage = require('./power_usage'); const pu = new power_usage(dbConn); pu.readPoints(callBack); pu.writePoint(dataPoints, callBack);
After coding the power_usage
metric class, create the application's entry point in the next step.
4. Create the Application's Entry Point
Every Node.js application must have an entry point. This is the main file that runs when the application starts. The sample application in this guide runs through an index.js
file. Create the file by following the steps below.
Open a new
index.js
file for editing purposes.$ nano index.js
Enter the following information into the
index.js
file. Replace172.16.0.1
with your server's private IP address.const http = require('http'); const influxdb_gateway = require('./influxdb_gateway'); const power_usage = require('./power_usage'); const httpHost = '172.16.0.1'; const httpPort = 8080; const server = http.createServer(httpHandler); server.listen(httpPort, httpHost, () => { console.log(`Server running at http://${httpHost}:${httpPort}/`); }); function httpHandler(req, res) { const dg = new influxdb_gateway(); const dbConn = dg.dbConn(); const pu = new power_usage(dbConn); res.statusCode = 200; res.setHeader('Content-Type', 'text/html'); if (req.method == 'POST') { var httpPayload = ""; req.on('data', function (data) { httpPayload = data; }); req.on('end', function () { pu.writePoint(JSON.parse(httpPayload), writeCallBack); }); } function writeCallBack(response) { res.write(response 'rn'); res.end(); } if (req.method == 'GET') { pu.readPoints(readCallBack); } function readCallBack(dataPoints, dataColumns) { var tableHeaders = ''; for (var columnIndex in dataColumns) { tableHeaders = '<th>' dataColumns[columnIndex] '</th>'; } var rows = ''; for (var rowIndex in dataPoints) { var rowData = '<tr>'; for (var columnIndex in dataPoints[rowIndex]) { rowData = '<td>' dataPoints[rowIndex][columnIndex] '</td>'; } rowData = '</tr>'; rows = rowData; } const tableData = "<table border = '1' align = 'center'><tr>" tableHeaders "</tr>" rows "</table>"; const html = `<!DOCTYPE html> <html> <body align = 'center'> <h1>Power Usage For Customers</h1><hr>` tableData `</body> </html>`; res.write(html); res.end(); } }
Save and close the
index.js
file.
The index.js
file explained:
The
...require(...)
section loads all the modules the application requires to run. Thehttp
module offers HTTP functionalities to your application and allows you to run a web server. Theinfluxdb_gateway
andpower_usage
statements load the custom classes you coded earlier.const http = require('http'); const influxdb_gateway = require('./influxdb_gateway'); const power_usage = require('./power_usage'); ...
The next four lines initialize a web server that listens for incoming connections under port
8080
. Then, thehttp.createServer(httpHandler);
statement passeshttpHandler
as the handler function for incoming HTTP requests.... const httpHost = '172.16.0.1'; const httpPort = 8080; const server = http.createServer(httpHandler); server.listen(httpPort, httpHost, () => { console.log(`Server running at http://${httpHost}:${httpPort}/`); }); ...
The
httpHandler(req, res)
function initializes theinfluxdb_gateway
and thepower_usage
modules using the following statements.... const dg = new influxdb_gateway(); const dbConn = dg.dbConn(); const pu = new power_usage(dbConn); ...
The next two lines set the correct HTTP headers for the application to allow HTTP clients to treat the data as HTML.
... res.statusCode = 200; res.setHeader('Content-Type', 'text/html'); ...
The logical
if (...){...}
statements examine the HTTP method to determine the appropriate method to run. ForPOST
requests (if (req.method == 'POST') {...}
), the application runs thepu.writePoint(JSON.parse(httpPayload), writeCallBack);
function to write the data points to the InfluxDB database. ForGET
requests (if (req.method == 'GET') {...}
), the application runs thepu.readPoints(readCallBack);
function to read the data points from the InfluxDB database.The
writeCallBack(...)
function executes when the application writes data successfully into the database. ThewriteCallBack(...)
function returns aSuccess.
message.... function writeCallBack(response) { res.write(response 'rn'); res.end(); } ...
The
readCallBack(dataPoints, dataColumns)
function runs when the application retrieves data from the InfluxDB database and displays the data in HTML format. Thefor (var columnIndex in dataColumns){...}
loop sets the HTML table headers. Then, thefor (var rowIndex in dataPoints) {...}
loop populates the HTML table rows.... function readCallBack(dataPoints, dataColumns) { ... for (var columnIndex in dataColumns) { tableHeaders = '<th>' dataColumns[columnIndex] '</th>'; } var rows = ''; for (var rowIndex in dataPoints) { var rowData = '<tr>'; for (var columnIndex in dataPoints[rowIndex]) { rowData = '<td>' dataPoints[rowIndex][columnIndex] '</td>'; } rowData = '</tr>'; rows = rowData; } ... res.write(html); res.end(); } ...
You now have all the required application's source code files. Test the application in the next step.
5. Test the Application
The final step in this guide involves the following:
Initializing the
project
directory.Installing the
influxdb-nodejs
module using thenpm
package.Inserting new data points using the Linux
curl
command.Displaying the InfluxDB data points on an HTML web page.
Follow the steps below to complete the above steps:
Ensure your Node.js
npm
package is up to date.$ sudo npm install npm -g
Output.
changed 54 packages, and audited 212 packages in 2s
Initialize the
project
directory.$ npm init
Respond to the following prompts. For most prompts, you only need to press the Enter to proceed.
package name: (project) :key_enter: version: (1.0.0):key_enter: description: InfluxDB and Node.js :key_enter: entry point: (index.js) :key_enter: test command: :key_enter: git repository: :key_enter: keywords: influxdb, node.js :key_enter: author: Test author :key_enter: license: (ISC) :key_enter: About to write to ... { ... } Is this OK? (yes) yes :key_enter:
Install the
influxdb-nodejs
module.$ npm install influxdb-nodejs
Output.
added 41 packages, and audited 42 packages in 2s
Run the project.
$ node index.js
The application starts a web server that listens for incoming HTTP connections on port
8080
. The previous command has a blocking function. Don't run more commands on the active terminal window.Server running at http://172.16.0.1:8080/
Establish another
SSH
connection in a new terminal window and run the following commands.$ curl -X POST http://172.16.0.1:8080/ -H 'Content-Type: application/json' -d '{"customer_id": 6, "KWh": 2687}' $ curl -X POST http://172.16.0.1:8080/ -H 'Content-Type: application/json' -d '{"customer_id": 7, "KWh": 1265}' $ curl -X POST http://172.16.0.1:8080/ -H 'Content-Type: application/json' -d '{"customer_id": 8, "KWh": 4268}' $ curl -X POST http://172.16.0.1:8080/ -H 'Content-Type: application/json' -d '{"customer_id": 9, "KWh": 1398}' $ curl -X POST http://172.16.0.1:8080/ -H 'Content-Type: application/json' -d '{"customer_id": 10, "KWh": 7625}'
Output.
... Success.
Open a web browser and visit the following URL. Replace
172.16.0.1
with your server's public IP addressThe following HTML table lists the InfluxDB data points.
Conclusion
This guide shows you how to implement the InfluxDB server with Node.js on Ubuntu 20.04 server. The sample application only uses a few data points for demonstration purposes. However, InfluxDB has rich grouping and mathematical functions that you can use to summarize the data when designing more detailed applications.
Follow the link below to read how to use the InfluxDB server with Golang: