Common Database Interface for Node
Database-js creates the structure for a common database interface. It's roughly structured on Java's database interface. It has built in prepared statements, even if the underlying driver does not support them. It is built on Promises so it works well with ES7 async code.
Database-js uses the concept of a connection string. The format of the string is:
driver://[[[username]:[password]@]host]/database[?other parameters]
Since drivers must have a common interface, there are a few wrappers around drivers:
database-js-mysql://[[username]:[password]@]host/database
database-js-adodb:///file_path[?parameters]
database-js-sqlite:///file_path
Yes, it is an SQL wrapper for a NoSQL database.
database-js-firebase://username:password@project_id/root_node?apiKey=API_KEY
Where the root node is the path to the parent of the entries you define as tables. The project ID and API key can get found in your Firebase console.
var Database = require('database-js2').Connection;
(async function() {
let connection, statement, rows;
connection = new Database('database-js-mysql://my_secret_username:my_secret_password@localhost:3306/my_top_secret_database');
try {
statement = await connection.prepareStatement("SELECT * FROM tablea WHERE user_name = ?");
rows = await statement.query('not_so_secret_user');
console.log(rows);
} catch (error) {
console.log(error);
} finally {
await connection.close();
}
})();
var Database = require('database-js2').Connection;
(async function() {
let connection, statement, rows;
connection = new Database('database-js-adodb:///C:\\Users\\me\\Desktop\\database.mdb');
try {
statement = await connection.prepareStatement("SELECT * FROM tablea WHERE user_name = ?");
rows = await statement.query('not_so_secret_user');
console.log(rows);
} catch (error) {
console.log(error);
} finally {
await connection.close();
}
})();
The example XLS file is included in this package.
var Database = require('database-js2').Connection;
(async function() {
let connection, statement, rows;
connection = new Database('database-js-adodb:///C:\\Users\\me\\Desktop\\database.xls?Extended Properties='Excel 8.0;HDR=Yes;IMEX=1';');
try {
statement = await connection.prepareStatement("SELECT * FROM [Sheet1$A1:C52] WHERE State = ?");
rows = await statement.query('South Dakota');
console.log(rows);
} catch (error) {
console.log(error);
} finally {
await connection.close();
}
})();
The username and password is not the Firebase account holder's email and password. These are the login credentials for an authorized user of the Firebase data. Currently the wrapper only supports email sign in methods.
This example is live, but read-only, if you install the normal Firebase package, you can use the API key and credentials below to examine the data the Firebase way and compare it to this SQL wrapper.
Try this in the browser with RunKit
var Database = require('database-js2').Connection;
(async function() {
let connection, statement, rows;
connection = new Database('database-js-firebase://user@example.com:password@statesdemo/ewJviY6wboTKJ57A2dZkvq8kxYo1?apiKey=AIzaSyD1ypTmnJb_d8ZOyfc-KBMe0tw8owYCwjA');
try {
statement = await connection.prepareStatement("SELECT * FROM states WHERE State = ?");
rows = await statement.query('South Dakota');
console.log(rows);
} catch (error) {
console.log(error);
} finally {
await connection.close();
}
})();
Memory only SQLite databases are not yet supported, a filename must be passed. The example database is included in this package, so this should be able to run.
var Database = require('database-js2').Connection;
(async () => {
let connection, statement, rows;
connection = new Database('database-js-sqlite:///test.sqlite');
try {
statement = await connection.prepareStatement("SELECT * FROM states WHERE State = ?");
rows = await statement.query('South Dakota');
console.log(rows);
} catch (error) {
console.log(error);
} finally {
await connection.close();
}
})();
Writing a new wrapper around an existing database implementation in Node is fairly straight forward. The database-js-mysql wrapper is a good place to start. Generally the pattern will be like this:
var baseDriver = require('base-driver');
var m_connection = Symbol('connection');
class Wrapper {
constructor(connection) {
this[m_connection] = connection;
}
query(sql) {
var self = this;
return new Promise((resolve, reject) => {
self[m_connection].query(sql, (error, data, fields) => {
if (error) {
reject(error);
} else {
resolve(data);
}
});
});
}
execute(sql) {
return new Promise((resolve, reject) => {
self[m_connection].execute(sql, (error, data, fields) => {
if (error) {
reject(error);
} else {
resolve(data);
}
});
});
}
close() {
var self = this;
return new Promise((resolve, reject) => {
self[m_connection].close((err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
}
module.exports = {
open: function(connection) {
let base = baseDriver.createConnection({
host: connection.Hostname || 'localhost',
port: parseInt(connection.Port) || 3306,
user: connection.Username,
password: connection.Password,
database: connection.Database
})
return new Wrapper(base);
}
};
The database-js-firebase wrapper is an example of adding an SQL interface to a data store that is not SQL based. It uses node-sqlparser by fish to parse the SQL statement into an object which is easy to work with. Someone could easily create a wrapper around an HTTP server, or even create an SQL interface to IoT sensors.