A Simple Web Server in Node.js with a MariaDB Backend

Follow the steps given below to implement a simple web server in Node.js that, upon receiving a request, reads a list of databases from an underlying MariaDB server and returns a list of them as the web response.

I actually built a more complicated “RESTful” version of this server in a previous post, but upon reviewing that post recently, it actually seemed a little too complicated. This version is hopefully a little easier to understand and to adapt.

UPDATE 2016/04/11 – Since this article was originally posted, there have been some irritating changes to mariasql and Node.js itself that make it nearly impossible to get mariasql running in my development environment. More specifically, mariasql seems to depend on something called “node-gyp” that seems to be incompatible with the latest version of Node.js, or something like that. I have created an alternate version of this server that uses the “mysql” package instead.

MariaDBDatabasesInBrowser

1. If you have not already done so, download and install Node.js, then install the necessary packages using the “npm install” utility. Details are given in a previous post, but the most important command to run is “npm install mariasql” (or, if you’re using the alternative code listing, “npm install mysql”).

2. If you have not already done so, download and install MariaDB. Details are given in a previous post. When prompted, make the “root” user’s password “Password42”, or, if you choose a different password, make the appropriate substitution in the JavaScript code listing on a subsequent step.

3. In any convenient location, create a new text file named “NodeJSServerMariaDBTest.js”, containing one of the two code listings that appear below the main body of this message. The first uses “mariasql” package, which has become problematic since this article was originally posted, while the second uses the “mysql” package instead.

4. Open a console window and enter the command “node NodeJSServerMariaDBTest.js” to start the web server.

5. Open a web browser and go to “http://localhost:1337”. A list of the databases on the MariaDB server should appear.

// includes

var http = require("http");
var inspect = require("util").inspect;
var MariaSQLClient = require("mariasql");

// main

function main()
{
	var database = new Database
	(
		"127.0.0.1", // host
		"root", // username
		"Password42" // password
	);

	var webServer = new WebServer
	(
		"127.0.0.1", // hostAddress
		1337, // portNumber
		database
	);

	webServer.start();
}

// classes

function Database(host, username, password)
{
	this.host = host;
	this.username = username;
	this.password = password;
}
{
	Database.prototype.connect = function()
	{
		this.client = new MariaSQLClient();
		this.client.connect
		(
			{
		  		host: this.host,
		  		user: this.username,
		  		password: this.password
			}
		);

		this.client.on
		(
			"connect", 
			this.handleEventClientConnect.bind(this)
		).on
		(
			"error", 
			this.handleEventClientError.bind(this)
		).on
		(
			"close",
			this.handleEventClientClose.bind(this)
		);
	}

	Database.prototype.query = function(queryText, callback, thisForCallback)
	{
		this.connect();

		this.client.query(queryText).on
		(
			"result", 
			this.handleEventQueryResult.bind(this, callback, thisForCallback)
		).on
		(
			"end", 
			this.handleEventQueryEnd.bind(this)
		);

		this.client.end();
	}

	// events

	Database.prototype.handleEventClientClose = function(hadError) 
	{ 
		console.log("Client closed."); 
	}

	Database.prototype.handleEventClientConnect = function() 
	{ 
		console.log("Client connected."); 
	}

	Database.prototype.handleEventClientError = function(error)
	{
		console.log("Client error: " + error);
	}

	Database.prototype.handleEventQueryEnd = function() 
	{ 
		console.log("Done with all results."); 
	}

	Database.prototype.handleEventQueryResult = function
	(
		callback, thisForCallback, result
	) 
	{
		var rowsRetrieved = [];

		result.on
		(
			"row", 
			this.handleEventQueryResultRow.bind
			(
				this, 
				rowsRetrieved
			)
		).on
		(
			"error", 
			this.handleEventQueryResultError.bind(this)
		).on
		(
			"end", 
			this.handleEventQueryResultEnd.bind
			(
				this, rowsRetrieved, callback, thisForCallback
			)
		);
	}

	Database.prototype.handleEventQueryResultEnd = function
	(
		rowsRetrieved, callback, thisForCallback, info
	) 
	{ 
		console.log("Result finished successfully."); 
		callback.call(thisForCallback, rowsRetrieved);
	}

	Database.prototype.handleEventQueryResultError = function(error) 
	{ 
		console.log("Result error: " + inspect(error)); 
	}

	Database.prototype.handleEventQueryResultRow = function(rowsRetrieved, row) 
	{ 
		var rowAsString = inspect(row);
		rowsRetrieved.push(rowAsString);
	}
}

function WebServer(hostAddress, portNumber, database)
{
	this.hostAddress = hostAddress;
	this.portNumber = portNumber;
	this.database = database;

	this.server = http.createServer
	(
		this.handleRequest.bind(this)
	);
}
{
	WebServer.prototype.start = function()
	{
		this.server.listen(this.portNumber, this.hostAddress);

		console.log
		(
			"Server running at http://" 
			+ this.hostAddress + ":" 
			+ this.portNumber + "/"
		);
	}

	// events

	WebServer.prototype.handleRequest = function(webRequest, webResult) 
	{
		if (webRequest.url == "/favicon.ico")
		{
			// Ignore favicon requests.
			return;
		}

		this.database.query
		(
			"show databases",
			this.handleRequest_QueryComplete.bind(this, webResult),
			this // thisForQuery
		);
	}

	WebServer.prototype.handleRequest_QueryComplete = function(webResult, rowsRetrieved)
	{
		webResult.writeHead
		(
			200, // OK
			{"Content-Type": "text/plain"}
		);
	
		var resultContent = rowsRetrieved.join("\n");

		console.log("Result: " + resultContent);
	
		webResult.end(resultContent);
	}
}

// run

main();
// includes

var http = require("http");
var inspect = require("util").inspect;
var mysql = require("mysql");

// main

function main()
{
	var database = new Database
	(
		"127.0.0.1", // host
		"root", // username
		"Password42", // password
		"mysql" // databaseName
	);

	var webServer = new WebServer
	(
		"127.0.0.1", // hostAddress
		1337, // portNumber
		database
	);

	webServer.start();
}

// classes

function Database(host, username, password, databaseName)
{
	this.host = host;
	this.username = username;
	this.password = password;
	this.databaseName = databaseName;
}
{
	Database.prototype.connect = function()
	{
		this.connection = mysql.createConnection
		({
			host     : this.host,
			user     : this.username,
			password : this.password,
			database : this.databaseName
		});
	}

	Database.prototype.query = function(queryText, callback, thisForCallback)
	{
		this.connection.connect();

		this.connection.query
		(
			queryText,
			this.handleEventQueryResult.bind(this, callback, thisForCallback)
		);

		this.connection.end();
	}

	// events

	Database.prototype.handleEventQueryResult = function
	(
		callback, thisForCallback, error, rowsRetrieved, fields
	) 
	{
		if (error) throw error;

		callback.call(thisForCallback, rowsRetrieved, fields);
	}
}

function WebServer(hostAddress, portNumber, database)
{
	this.hostAddress = hostAddress;
	this.portNumber = portNumber;
	this.database = database;

	this.server = http.createServer
	(
		this.handleRequest.bind(this)
	);
}
{
	WebServer.prototype.start = function()
	{
		this.server.listen(this.portNumber, this.hostAddress);

		console.log
		(
			"Server running at http://" 
			+ this.hostAddress + ":" 
			+ this.portNumber + "/"
		);
	}

	// events

	WebServer.prototype.handleRequest = function(webRequest, webResult) 
	{
		if (webRequest.url == "/favicon.ico")
		{
			// Ignore favicon requests.
			return;
		}

		this.database.connect();

		this.database.query
		(
			"show databases",
			this.handleRequest_QueryComplete.bind(this, webResult),
			this // thisForQuery
		);
	}

	WebServer.prototype.handleRequest_QueryComplete = function(webResult, rowsRetrieved, fields)
	{
		webResult.writeHead
		(
			200, // OK
			{"Content-Type": "text/plain"}
		);

		var resultContent = "";

		for (var r = 0; r < rowsRetrieved.length; r++)
		{
			var row = rowsRetrieved[r];

			for (var f = 0; f < fields.length; f++)
			{
				var fieldName = fields[f].name;
				var fieldValue = row[fieldName];
				
				resultContent += fieldValue + " ";
			}

			resultContent += "\n";
		}

		console.log("Result: " + resultContent);
	
		webResult.end(resultContent);
	}
}

// run

main();
This entry was posted in Uncategorized and tagged , , , , , , . Bookmark the permalink.

2 Responses to A Simple Web Server in Node.js with a MariaDB Backend

  1. Göran says:

    HI, found your article when I was looking for solutions to my increasing mariadb connections when using node-mariadb. Why are you using prototype on the left side? Why not just use the function. What is this. doing? Is it working as a private scoope? Why not use var.

  2. All the instances of “prototype” in the code are to make use of JavaScript’s support for object-oriented programming. If you’re unfamiliar with object orientation, it’s basically the grouping of data structures with the functions that operate on them. Object orientation can help to make a complicated program more understandable and intuitive. For more details, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s