Node MySQL - reusable connection module - javascript

Question Updated:
The solution should detail a simplified, proper model of a Node connection module allowing for the re-use of the connection by any module of a Node application needing to connect to a database. That way, the question might be useful for anyone with connection module issues in Node.
The answer might even include a way to pass in different credentials in order to connect to different tables or databases with a single function call from anywhere in an app.
I wrote a script that utilized a number of simple modules to allow a user to post login data, have that data validated on the server, and if correct, receive a response of success. A very basic login functionality.
The problem: One user can log-in, but any more log-in attempts before I restart the server fail to connect to the database.
It appears that, because I'm declaring the connection in a variable in my module db_connect and requiring that module, the connection can't be re-used. It's being declared in the required module, and I had mistakenly believed that calling the variable in each connection attempt would recreate the connection. It doesn't.
The solution: As suggested by barry in the comments, in the db_connect module, I need to make the connection functionality a function rather than a variable, so I can then create the connection from within my validation script.
How can I do this? I'm trying to output the connection object when calling the createConnection() function, which is an exported method of db_connect.
db_connect:
console.log('db_connect module initialized');
var mysql = require('mysql');
function createConnection(){
var connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password : '',
database : 'officeball'
});
}
exports.createConnection = createConnection();
exports.mysql = mysql;
validator:
console.log('validator module initialized');
var connect = require("./db_connect");
function validate(username, password, callback){
var createConnection = connect.createConnection();
//the idea is for this to return the object, connection,
//which opens a new connection
connection.connect(function (err){
if (err) return callback(new Error('Failed to connect'), null);
console.log('Connection with the Officeball MySQL database openned...');
connection.query('select username,password,fname,lname,rank,active from users where username=?',
username,
function(err,rows,fields) {
connection.destroy();
console.log('...Connection with the Officeball MySQL database closed.');
if (err)
return callback(new Error ('Error while performing query'), null);
if (rows.length !== 1)
return callback(new Error ('- [Anomaly] - Failed to find exactly one user'), null);
if (rows[0].password === password & rows[0].active === "yes") {
var result = new Object;
result.username = rows[0].username;
result.password = rows[0].password;
result.fname = rows[0].fname;
result.lname = rows[0].lname;
result.rank = rows[0].rank;
return callback(null, result);
} if(rows[0].active !== "yes"){
return callback(new Error ('User account not active.'), null);
}else {
return callback(new Error ('Login credentials did not match.'), null);
}
});
});
};
exports.validate = validate;
Console log (originally, there was a connection error, but after my fix-attempt, the error is about the method):
C:\xampp\htdocs\officeball\node_scripts>node index.js
application initialized
server module initialized
login module initialized
validator module initialized
db_connect module initialized
sale module initialized
Server running at http://127.0.0.1:8080/
User username1 is attempting login...
TypeError: Property 'createConnection' of object #<Object> is not a function
at Object.validate (C:\xampp\htdocs\officeball\node_scripts\custom_modules\v
alidator.js:6:33)
at C:\xampp\htdocs\officeball\node_scripts\custom_modules\login.js:61:13
at callbacks (C:\xampp\htdocs\officeball\node_scripts\node_modules\express\l
ib\router\index.js:164:37)
at param (C:\xampp\htdocs\officeball\node_scripts\node_modules\express\lib\r
outer\index.js:138:11)
at pass (C:\xampp\htdocs\officeball\node_scripts\node_modules\express\lib\ro
uter\index.js:145:5)
at Router._dispatch (C:\xampp\htdocs\officeball\node_scripts\node_modules\ex
press\lib\router\index.js:173:5)
at Object.router (C:\xampp\htdocs\officeball\node_scripts\node_modules\expre
ss\lib\router\index.js:33:10)
at next (C:\xampp\htdocs\officeball\node_scripts\node_modules\express\node_m
odules\connect\lib\proto.js:193:15)
at multipart (C:\xampp\htdocs\officeball\node_scripts\node_modules\express\n
ode_modules\connect\lib\middleware\multipart.js:93:27)
at C:\xampp\htdocs\officeball\node_scripts\node_modules\express\node_modules
\connect\lib\middleware\bodyParser.js:64:9

Your problem was primarily what I mentioned in my comment, so I will make it an answer. Don't hurt yourself slapping your forehead. :-) See my inline comments below. You had one method invocation too many, an unreturned result, and a misnamed variable - otherwise, it works just fine.
db_connect.js
console.log('db_connect module initialized');
var mysql = require('mysql');
function createConnection(){
var connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password : '',
database : 'officeball'
});
// ************ NOTE BELOW FOR CHANGE **************
// You didn't return anything from this function. You need to return the connection
return connection;
}
// ************ NOTE BELOW FOR CHANGE **************
// You are exporting a single connection by invoking createConnection();
// exports.createConnection = createConnection();
// what you want is:
exports.createConnection = createConnection;
exports.mysql = mysql;
validator.js
function validate(username, password, callback){
// ************ NOTE BELOW FOR CHANGE **************
// You had:
// var createConnection = connect.createConnection();
// but based on your code, you wanted to write this instead:
var connection = connect.createConnection();
/// ... REMAINDER OMITTED, because it was A-OK and this is already a long page
};
If you make those two three changes, you should be good to go. As ever, feel free to ask for any clarifications if it would be helpful.
Update This is how I am calling it - as you can see, I had it try to do it ever 2 seconds.
jt-test.js
var v = require('./validator');
var timers = require('timers');
var connections = 0;
timers.setInterval(function(){
v.validate('bagehot','foo',function(err,result){
if (err)
console.log('failed', err);
else
console.log('success! ',result);
});
},2000);
result
Connection with the Officeball MySQL database openned...
...Connection with the Officeball MySQL database closed.
success! { username: 'bagehot',
password: 'foo',
fname: 'walter',
lname: 'bagehot',
rank: 12 }
Connection with the Officeball MySQL database openned...
...Connection with the Officeball MySQL database closed.
success! { username: 'bagehot',
password: 'foo',
fname: 'walter',
lname: 'bagehot',
rank: 12 }
Connection with the Officeball MySQL database openned...
...Connection with the Officeball MySQL database closed.
success! { username: 'bagehot',
password: 'foo',
fname: 'walter',
lname: 'bagehot',
rank: 12 }
Connection with the Officeball MySQL database openned...
...Connection with the Officeball MySQL database closed.
success! { username: 'bagehot',
password: 'foo',
fname: 'walter',
lname: 'bagehot',
rank: 12 }
And so on. It runs indefinitely. Complete code for the three files is in this gist

You can checkout my emysql package.
https://www.npmjs.org/package/emysql
https://github.com/yanke-guo/enhanced-mysql-pool
Maybe it's some kind of outdated with the most recent node-mysql package, but still functional.

Related

Auth0 Real-time Webtask Logs Extension not working and user not getting stored in database

I am using the Auth0 Real-time Webtask Logs Extension to debug the create action script of my custom database. I have placed a few console log statements in the script, but nothing is being outputted in the Real-time Webtask Logs. I have already installed and configured the extension in my Auth0 dashboard, and I have selected the correct tenant in the Real-time Webtask Logs Extension console. I have also checked the logs in the Auth0 dashboard to make sure that the script is being executed and that there are no errors. However, the output from the console log statements is still not being displayed in the Real-time Webtask Logs Extension console.
The other problem is that the user is not getting stored in the database after registration. The script seems to be executing correctly as I am able to receive the authResult that is returned by the callback of auth0.WebAuth.signup() method, but the user is not being added to the database. I am not sure if these two issues are related or if there is a separate issue with the database connection or the insert query.
Here's what my signup code looks like:
const auth0 = new auth0.WebAuth({
clientID: 'MY_CLIENT_ID',
domain: 'MY_AUTH0_DOMAIN'
});
auth0.WebAuth.signup({
connection: 'Username-Password-Authentication',
email: 'user#example.com',
password: 'password',
userMetadata: { // this will be stored in user_metadata
firstName: 'John',
lastName: 'Doe'
}
}, function(err, result) {
if (err) {
console.error(err);
} else {
console.log(result);
}
});
Here is what my create action script looks like:
function create(user, callback) {
const mysql = require('mysql');
const bcrypt = require('bcrypt');
const connection = mysql.createConnection({
host: 'host',
user: 'user',
password: 'pwd',
database: 'database',
port: 8000,
});
console.log("test");
connection.connect();
const query = 'INSERT INTO users SET ?';
bcrypt.hash(user.password, 10, function(err, hash) {
const insert = {
fname: user.firstName,
lname: user.lastName,
email: user.email,
password: hash,
};
connection.query(query, insert, function (err, results) {
console.log("saving");
if (err) return callback(err);
if (results.length === 0) return callback();
console.log("success");
callback(null);
});
});
}
FYI, when I test the script using Auth0's Save and Try feature, the user gets stored in the database. Also, the login script works without any issues.
How can I fix these issues and ensure that the Real-time Webtask Logs Extension works and the user gets stored in the database after registration?

Node.Js MSSQL Query Timeout Expired

I am using Node Express API to run SQL queries to populate a dashboard of data. I am using the mssql-node package to do so. Sometimes it runs flawlessly, other times I get the following error:
[Error: [Microsoft][SQL Server Native Client 11.0]Query timeout expired]
I am creating a poolPromise with a connectionPool to the db, then I pass that object to my other controllers which run the specific queries to populate data. I run the server which initiates the db.js script and connects to MSSQL with a pool connection.
db.js:
// for connecting to sql server
const sql = require('mssql/msnodesqlv8');
// db config to connect via windows auth
const dbConfig = {
driver: 'msnodesqlv8',
connectionString: 'Driver={SQL Server Native Client 11.0};Server={my_server};Database={my_db};Trusted_Connection={yes};',
pool: {
idleTimeoutMillis: 60000
}
};
// create a connectionpool object to pass to controllers
// this should keep a sql connection open indefinitely that we can query when the server is running
const poolPromise = new sql.ConnectionPool(dbConfig)
.connect()
.then(pool => {
console.log('Connected to MSSQL');
return pool;
})
.catch(err => console.log('Database Connection Failed! Bad Config: ', err))
module.exports = { sql, poolPromise };
An example of one of my controllers and how I use the poolPromise object is below. I currently have about 7 of these controllers that run their own specific query to populate a specific element on the dashboard. The performance of the queries each run within 1-10 seconds (depending on current server load, as I am querying an enterprise production server/db, this can vary). As I mentioned earlier, the queries run flawlessly sometimes and I have no issues, but at other times I do have issues. Is this a symptom of me querying from a shared production server? Is it preferred to query from a server that has less load? Or am I doing something in my code that could be improved?
const { sql, poolPromise } = require('../db');
// function to get data
const getData = async (req, res) => {
try {
// create query parameters from user request
let id= req.query.id;
// create query from connectionPool
let pool = await poolPromise;
let qry = `
select * from tbl where id = #Id
`
let data = await pool.request()
.input('Id', sql.VarChar(sql.MAX), id)
.query(qry);
// send 200 status and return records
res.status(200);
res.send(data.recordset);
} catch(err) {
console.log('Error:');
console.log(err);
res.sendStatus(500);
}
};
module.exports = { getData };

Insert multiple records into SQL Server (mssql) in node.js

I am migrating a piece of code from mysql to mssql package of nodejs, in which its required to insert multiple rows.
Here's the sample code I am using for testing:
const db = require('mssql');
let config = {
user: 'salim',
password: 'admin',
server: 'LAPTOP-JK45R', // You can use 'localhost\\instance' to connect to named instance
database: 'master',
}
var pool;
//initialize connection pool
var connectPool = initializeConnectionPool();
function initializeConnectionPool() {
pool = new db.ConnectionPool(config);
pool.on('error', (err) => {
logger.error(err);
});
return pool.connect();;
}
connectPool.then(async () => {
let connection = await pool.request();
console.log('Got pool connection...');
var q = "INSERT INTO Person (name, address) VALUES ?";
var values = [
['John', 'Highway 71'],
['Peter', 'Lowstreet 4'],
['Amy', 'Apple st 652'],
['Hannah', 'Mountain 21']
];
let result = await connection.query(q,[values]);
console.log(`Result: ${JSON.stringify(result)}`);
});
Its giving me error:
RequestError: Incorrect syntax near '?'.
I couldn't find any thing on official npm page of mssql, so I have been trying this: Insert multiple columns and rows into SQL Server with node js
In my code I am just using pool.
I also couldn't find how to log queries using this package, so couldn't figure out what the query is being formed.
It would be great to know any of the solution.
The ? is a way to pass parameter in mysql
In mssql it seem to be like ${entries}
refer to How to pass parameter to mssql query in node js

Sails.js / Waterline .add() and .remove() only works on second call

I'm trying to use a Many-to-Many association between 2 models and I have to call .add() or .remove() twice for it to work properly:
// User.js Model
module.exports = {
attributes: {
connections: {
collection: 'connection',
via: 'users'
}
}
}
// Connection.js Model
module.exports = {
attributes: {
users: {
collection: 'user',
via: 'connections'
}
}
}
This is the code I have in my UsersController.js:
User.findById(req.params.user)
.populate('connections')
.exec(function(err, users) {
if (err) return res.status(404).end(); // Not really a 404 but good for now
var user = users[0];
user.connections.add(req.body.connection_id);
user.save(function(err) {
// In my tests err is null/undefined
return res.status(204).end();
}
});
I get a 204 back with no errors in the log or nothing else failed. When I check in my MongoDB database, the user's connections field is just an empty array. If I send the same request again, then I get a an error saying the connection_id already exists for that user. I look into the database, and now the connection appears in the user's connections field. It does the same thing when I remove a connection, I have to send it twice for it to take effect. Anyone have any ideas?
Here are the module versions I'm using:
Node.js version: 0.12.0
Sails.js version: 0.11.0
sails-mongo version: 0.10.5
Sounds like something related to synchronous vs asynchronous methods... meaning you are exiting the method before a certain request is completed. However not sure where that is happening. See if this helps:
var connectionId = req.param('connection_id');
var userId = req.param('user');
User.find(userId)
.populate('connections')
.exec(function(err, users) {
if (err) return res.status(404).end(); // Not really a 404 but good for now
//BTW might be good to double check doing console.log("users: "+JSON.stringify(users));
var user = users[0];
user.connections.push(connectionId);
//Is it really there? console.log("user: "+JSON.stringify(user));
user.save(function(err) {
// In my tests err is null/undefined
return res.status(204).end();
}
});

How do I call a function defined in app.js from my ejs template?

I have a function defined in app.js
var addUser = function(uname, pword, e_mail, name, callback){
var coll_model = mongoose.model('collaborator', User);
var collaborator = new coll_model(
{
username: uname,
password : pword,
email : e_mail,
fullName : name
});
collaborator.save(function (err){
if(err)
{
console.log('There was an error creating the user');
}
});
callback(collaborator._id);
};
I want to be able to call this from my ejs template.
How does one go about this?
Thanks in advance
This is a server side operation. You should never invoke a function on the client that manipulates data directly at the database.
As you are using node and monogoDB, you could consider using express as your webframework.
You can read about the communication between client -> server -> database here: http://coenraets.org/blog/2012/10/creating-a-rest-api-using-node-js-express-and-mongodb/

Categories