node-mysql-wrapper
Table of Contents
Install
$ npm install node-mysql-wrapper
Sometimes I may also ask you to install the latest version from Github to check
if a bugfix is working. In this case, please do:
$ npm install nodets/node-mysql-wrapper
Introduction
This is a node.js wrapper for node-mysql driver package. It is written in TypeScript, does not
require compiling (all JavaScript files you need are inside the 'compiled' folder).
Here is an example on how to use it:
var mysql = require('mysql');
var connection = mysql.createConnection({
host : 'localhost',
user : 'me',
password : 'secret',
database : 'my_db'
});
var wrapper = require('node-mysql-wrapper');
var db = wrapper.wrap(connection);
import {wrap} from "node-mysql-wrapper";
let db = wrap(connection);
db.ready(function(){
db.table("users").findById(8,function(user){
console.dir(user);
db.destroy();
});
});
From this example, you can learn the following:
- Every method you invoke on a table is queued and executed in asynchronous way, using callbacks and/or promises.
- Closing the connection is done using
destroy()
which makes sure all remaining
queries are executed before sending a quit packet to the mysql server.
Contributors
Thanks goes to the people who have contributed code to this module, see the
GitHub Contributors page.
If you'd like to discuss this module, or ask questions about it, please use one
of the following:
Establishing connections
The recommended way to establish a wrapped-connection is this:
var wrapper = require('node-mysql-wrapper');
var db = wrapper.wrap("mysql://user:pass@127.0.0.1/databaseName?debug=false&charset=utf8");
db.ready(function(){
});
However, a wrapped-connection can also be implicitly established by wrapping an existing mysql connection:
var mysql = require('mysql');
var connection = mysql.createConnection({
host : 'localhost',
user : 'me',
password : 'secret',
database : 'my_db'
});
var wrapper = require('node-mysql-wrapper');
var db = wrapper.wrap(connection);
db.ready(function(){
});
Depending on how you like to code, either method may be
appropriate. But in order to works always use .ready and a callback inside it.
Connection options
Read them at the node-mysql module documentation
Terminating connections
There are two ways to end a connection. Terminating a connection gracefully is
done by calling the end()
method:
db.end(function(err) {
});
This will make sure all previously enqueued queries are still before sending a
COM_QUIT
packet to the MySQL server. If a fatal error occurs before the
COM_QUIT
packet can be sent, an err
argument will be provided to the
callback, but the connection will be terminated regardless of that.
An alternative way to end the connection is to call the destroy()
method.
This will cause an immediate termination of the underlying socket.
Additionally destroy()
guarantees that no more events or callbacks will be
triggered for the connection.
db.destroy();
Unlike end()
the destroy()
method does not take a callback argument.
Tables
Manual select which tables you want to use. (default all)
db.useOnly('users','comments',['or_an_array_of_tables','comment_likes']);
Getting a table object
var usersTable = db.table("users");
console.log('available columns: '+ usersTable.columns);
console.log('primary key column name: '+ usersTable.primaryKey);
console.log('find, findById, findAll, save and remove methods can be called from this table');
usersTable.find({mail:"= makis@omakis.com"},function(results){
});
Performing queries
Method queries
They are 4 types of method queries, the find/findById/findSingle,findAll (select), save (insert or update), remove. All values you pass there are auto-escaped to protect the database from sql injections.
All methods return promises.
Column keys are auto converted to object properties, this means that user_id column on database table will be available as userId, same for table names too.
Simple 'find by id' method , this find method always returns one result.
db.table("users").findById(18,function(user){
console.log("SELECT * FROM users WHERE user_id = 18");
console.dir(user);
});
Simple 'find single' method , this find method always returns one result.
db.table("users").findSingle({mail:'= makis@ideopod.com'},function(user){
console.log("SELECT * FROM users WHERE mail = 'makis@ideopod.com' LIMIT 1");
console.dir(user);
});
Simple 'find by' method this find method always returns an array.
db.table("users").find({yearsOld:'>= 18'},function(users){
console.log("SELECT * FROM users WHERE yearsOld >= 18. results: ";
console.dir(users);
});
Simple 'find all' method , which is the same as db.table("tablename").find({},callback); or .find({ tableRules : { limit :42 }} if rules passed.
db.table("users").findAll({limit :42}).then(function(users){
console.log("SELECT * FROM users LIMIT 42. results: ");
console.dir(users);
});
An 'advanced find' method. Find all users where years_old = 22, find the user's info, find user's comments, the comment's likes and users who liked them.
var dbUsers = db.table("users");
var criteria = dbUsers.criteria
.where("yearsOld").gt(18)
.exclude("password","createdDate")
.joinAs("info","userInfos","userId").at("info").limit(1)
.parent()
.join("comments","userId")
.at("comments")
.orderBy("commentId",true)
.joinAs("likes","commentLikes","commentId")
.at("likes")
.joinAs("likers","users","userId").build();
dbUsers.find(criteria,function(results){
console.log('A lot of queries executed here, you can guess them :)');
console.dir(results);
});
Save method, Returns a single object, also updates the variable you pass into.
var newUser = { username: 'a new user', yearsOld: 23, mail: 'newmail@mail.com' };
db.table("users").save(newUser).then(function(result){
console.log('New user just created, new userId:'
+ result.userId+ ' which is the same as newUser.userId now:' +newUser.userId);
result.username = 'an updated new username';
db.table("users").save(result,function(){
console.log('User was just updated, because we have already a primary key setted at this object');
});
});
Remove method .1
db.table("users").remove({yearsOld:'= 22'},function(resultAnswer){
console.log(resultAnswer.affectedRows+ ' rows removed from '+resultAnswer.table);
});
Remove method .2 remove by id also
db.table("users").remove(4,function(results){
console.log('we were just deleted a user with userId(user_id) = 4');
});
Also you can wait for multiple query methods to finish before you do something using db.when method:
var findAUser = db.table("users").findById(16);
var findMoreUsers = db.table("users").find({username: '= a username'});
var findSomeComments = db.table("comments").find({userId:'= 16'});
db.when(findAUser,findMoreUsers,findSomeComments).then(function(results) {
});
Plain queries - the module's purpose is to never need to use this way.
To perform a plain custom query call the .query()
method on a wrapped-db object.
The simplest form of .query()
is .query(sqlString, callback)
, where a SQL string
is the first argument and the second is a callback:
db.query('SELECT * FROM `users` WHERE `user_id` = 18', function (error, results) {
});
( to escape a value here just use db.connection.escape(value) )
Stored procedures
You can call stored procedures from your queries as with any other mysql driver. If the stored procedure produces several result sets, they are exposed to you the same way as the results for multiple statement queries.
Make sure you create connection or wrap a connection with multipleStatements flag set to true: var db = wrap({ user: 'kataras', password: 'mypass', database: 'test', multipleStatements: true});
var myParams = "'arg1', 'arg2', ... ";
myParams.map(function(arg){ return db.connection.escape(arg);});
db.query("CALL MyStoredProcedure(" + myParams + ")", function(err, results, fields) {
if (err || results[0].res === 0) {
throw new Error("Error ... ");
} else {
}
});
var myParams = [];
myParams.push('arg1');
myparams.push('arg2');
try{
db.call('MyStoredProcedure', myParams, function(results, fields) {
console.log('Results returned: ',results);
});
}catch(ex){
console.log('Error info: ',ex);
}
Table events
on /watch
Each method/query will emit a table
event when a new type of query executed and parsed.
If you need to log or test results on the table before it gets used, you can
listen to these events: insert, update, remove (or delete) and save( for both insert and update) .
Note: Events are always executed before callbacks or promises.
var usersInsertWatcher = function(parsedInsertedRows){
console.log('Somewhere an insert query executed on USERS table : ');
console.dir(parsedInsertedRows);
};
var usersRemoveWatcher = function(deleted){
console.log('Somewhere a remove/delete query executed on USERS table : ');
console.log('Affected rows number: '+ deleted.affectedRows);
};
db.table("users").on('insert',usersInsertWatcher);
db.table("users").on('remove',usersRemoveWatcher);
off /unwatch
To turn off an event on a table just call db.table("tablename").off('event_type',the_callback_variable_to_remove)
db.table("users").off('insert',usersInsertWatcher);
db.table("users").off('remove',usersRemoveWatcher);
Extending a table
Any table can be extending to your own custom needs, with custom method queries, using this syndax:
db.table("tablename").extend('functionName',theFunction);
An example is always better, let's suppose that we have a users table with some columns, one of these columns is the 'mail' column, and we want to find if a user exists with a mail in our users table. Ofcourse we could use the already find method, but just for example purpose:
if(db.table("users").has('mailExists') === false)
{
db.table("users").extend('mailExists',function(mail,callback){
var q= "SELECT COUNT(*) FROM " + this.name + " WHERE mail = " +
db.connection.escape(mail);
db.query(q, function (err, results) {
if (!err && results.length > 0 && results[0]["COUNT(*)"] > 0) {
callback(true);
} else {
callback(false);
}
});
});
}
Use an extended method is simple
db.table("users").mailExists('an_e-mail@mail.com',function(exists){
if(exists) console.log('this mail already exists!');
else console.log('this mail doesnt exists.');
});
Watch database changes
Enable Binary Logging
First of all you have to enable binary logs in your MySQL Server,
In most of the cases this is enabled by default, but if not I will explain you how to enable it.
IF MYSQL SERVER VERSION IS GREATER OR EQUAL THAN 5.7 follow this:
IF OS === Windows
IF (MYSQL INSIDE xampp,wamp)
1. Open: C:/ xampp/ OR wamp/ mysql/YOUR_MYSQL_VERSION/my.ini
ELSE
1. Open explorer and write : %PROGRAMDATA%/MySQL/MySQL Server 5.7/my.ini
2. Go to the lines(119-120) which you can see these contents:
# Binary Logging.
# log-bin
3. Just uncomment the # log-bin, finall result must look like that:
# Binary Logging.
log-bin
4. Restart the mysql server service and you are ready.
ELSE IF OS === (L)Unix
IF (MYSQL INSIDE lampp)
1. Open explorer and open opt/lampp/etc/my.cnf
ELSE
1. Find where is my.cnf using these one of these shell commands:
locate my.cnf
whereis my.cnf
find . -name my.cnf
and open the my.cnf file.
2. Go to the line(119-120) which you can see these contents:
# Binary Logging.
# log-bin
3. Just uncomment the # log-bin, finall result must look like that:
# Binary Logging.
log-bin
4. Restart the mysql server service and you are ready.
ELSE IF MYSQL SERVER VERSION IS LESS OR EQUAL THAN 5.6
Watch on youtube this video: https://www.youtube.com/watch?v=xrTBFZyn-Bk
Example
This package supports BASIC live watch of your database changes,
make use of ObservableCollection.
var usersCollection = db.collection("users",function(){
usersCollection.onCollectionChanged(function(event,oldItems,newItems,oldStartingIndex,newStartingIndex){
});
});
For full support and the best experience you evern seen, take a look at nodets/node-mysql-live package.
Running tests
Import this database example to your local server, and have fan!
SET FOREIGN_KEY_CHECKS=0;
DROP TABLE IF EXISTS `comments`;
CREATE TABLE `comments` (
`comment_id` int(11) NOT NULL AUTO_INCREMENT,
`content` varchar(255) DEFAULT NULL,
`user_id` int(11) DEFAULT NULL,
PRIMARY KEY (`comment_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
INSERT INTO `comments` VALUES ('1', 'dsadsadsa', '18');
INSERT INTO `comments` VALUES ('2', 'wqewqewqeq', '18');
INSERT INTO `comments` VALUES ('3', 'cxxzczxczcz', '22');
INSERT INTO `comments` VALUES ('4', 'e comment belongs to 23 usersa', '23');
DROP TABLE IF EXISTS `comment_likes`;
CREATE TABLE `comment_likes` (
`comment_like_id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`comment_id` int(11) DEFAULT NULL,
PRIMARY KEY (`comment_like_id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
INSERT INTO `comment_likes` VALUES ('1', '18', '1');
INSERT INTO `comment_likes` VALUES ('3', '18', '2');
INSERT INTO `comment_likes` VALUES ('4', '12', '1');
INSERT INTO `comment_likes` VALUES ('5', '16', '3');
INSERT INTO `comment_likes` VALUES ('6', '18', '4');
INSERT INTO `comment_likes` VALUES ('7', '16', '4');
INSERT INTO `comment_likes` VALUES ('8', '16', '3');
INSERT INTO `comment_likes` VALUES ('9', '18', '3');
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`user_id` int(11) NOT NULL AUTO_INCREMENT,
`mail` varchar(255) DEFAULT NULL,
`username` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`created_date` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`years_old` int(11) DEFAULT '0',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5624 DEFAULT CHARSET=utf8;
INSERT INTO `users` VALUES ('16', 'an updated username for user_id 30 or 30 2nd time', 'an updated x username 1nd time', 'ewqeq', '2015-08-09 03:55:34', '21');
INSERT INTO `users` VALUES ('18', 'an updated mail for user id 18 2nd time', 'an updated username for user_id 18 3rd time', 'a pass', '2015-08-08 22:58:49', '55');
INSERT INTO `users` VALUES ('19', 'updated19@omakis.com', 'an 19 username', 'a pass', '2015-08-08 22:38:19', '22');
INSERT INTO `users` VALUES ('20', 'mail20_updated@omakis.com', 'an updated20 username', 'a pass', '2015-08-08 22:58:48', '15');
INSERT INTO `users` VALUES ('22', 'mail22@omakis.com', 'a username', 'a passing', '2015-08-08 22:38:13', '22');
INSERT INTO `users` VALUES ('23', 'mailwtf@dsadsa.com', 'a username', 'pass', '2015-08-08 22:38:16', '22');
INSERT INTO `users` VALUES ('28', 'an updated username for user_id 28 or 283rd time', 'an updated x username 2nd time', 'ewqewq', '2015-08-08 22:58:44', '15');
INSERT INTO `users` VALUES ('31', 'an updated username for user_id 31 or 31 2nd time', 'an updated x username 1nd time', 'dsadsada', '2015-08-09 03:55:32', '0');
INSERT INTO `users` VALUES ('5618', 'special@email.com', 'a special username', null, '2015-08-13 06:32:07', '23');
DROP TABLE IF EXISTS `user_infos`;
CREATE TABLE `user_infos` (
`user_info_id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`hometown` varchar(255) DEFAULT NULL,
PRIMARY KEY (`user_info_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
INSERT INTO `user_infos` VALUES ('1', '18', 'athens');
INSERT INTO `user_infos` VALUES ('3', '22', '22 user hometown');
INSERT INTO `user_infos` VALUES ('4', '23', '23 user hometown');
Todo
- Waiting for stable yield/*async support
Licence
This project is licensed under the MIT license.