NodeJs Connection Error querying SQL Server - sql-server

I am using this code to connect to my SQL Server and retrieve some data which works fine, if I only call the code once. If I call it twice I get this error:
ConnectionError: Already connecting to database! Call close before connecting to different database.at ConnectionPool._connect
But I am closing the conn after the call so I'm not sure what I am missing.
var sql = require('mssql');
const pool = new sql.ConnectionPool({
user: 'sa',
password: 'password',
server: '192.168.1.2',
database: 'demo',
options: {
encrypt: false
}
})
var conn = pool;
module.exports.getCounter = function( query){
conn.connect().then(function (err) {
var req = new sql.Request(conn);
req.query(query).then(function (result) {
console.log(result.recordset);
return result.recordset;
conn.close();
})
.catch(function (err) {
console.log(err);
conn.close();
});
})
.catch(function (err) {
console.log(err);
})};

You're returning the value before closing the connection, hence the function terminates before reaching that line. So just move the return statement below your conn.close(). The other issues you might have afterwards is that you might be calling your function twice before one executes and terminates completely, since those calls are asynchronous.
You might have to set your getCounter function as a Promise, so that you can wait for its completion/failure before calling it again. Off the top of my head in your example:
const getCounter = () => new Promise((resolve,reject) => {
conn.connect().then(function (err) {
var req = new sql.Request(conn);
req.query(query).then(function (result) {
conn.close();
resolve(result);
})
.catch(function (err) {
conn.close();
reject(err);
});
})
})
You can call your function afterwards as getCounter().then((result) => {...})

Here is another way to solve it which might be helpful for others.
const sql = require('mssql')
let connectionPoolConfig = {
user: 'sa',
password: 'password',
server: '192.168.1.2',
database: 'demo',
options: {
encrypt: false
}
}
let connectionPoolPromise = null
let connectionPoolObj = null
let getOrCreatePool = async () => {
if (connectionPoolObj) {
return connectionPoolObj
} else if (!connectionPoolPromise) {
connectionPoolPromise = new sql.ConnectionPool(connectionPoolConfig).connect()
}
connectionPoolObj = await connectionPoolPromise
return connectionPoolObj
}
let query = async(sql) => {
const pool = await getOrCreatePool()
return await pool.request().query(sql)
}
module.exports = {
query: query
}
And here is how to call it
let testCallerSQL = async () => {
try {
const res = await sqlUtil.query('select * from mytable')
console.log(res.recordset)
} catch(err) {
console.log(err)
} finally {
}
}

Related

Running Cypress with MSSQL- timing out

I want to clean a database before running my test cases and I'm just having issues with it running. It just times out and I don't understand - hopefully you can help me :)
In the test case I have the following code block:
beforeEach(() => {
cy.task("cleanUpDB", {
sql:"UPDATE SQL GOES HERE"
})
This then goes to my cypress.config file and the following is executed:
on("task", {
cleanUpDB({ theQuery }) {
return new Promise(async(resolve, reject) => {
try{
await sql.connect(DBConfig)
const result = await sql.query(theQuery);
console.log(result)
return resolve(result);
}
catch (err) {
// ... error checks
}
}
)
}
})
This is the error I get in the test runner:
You must use the same property name sql inside the task
beforeEach(() => {
cy.task("cleanUpDB", {
sql:"UPDATE SQL GOES HERE"
})
on("task", {
cleanUpDB({ sql }) { // since you wrap the parameter
... // you must use the property name sql
or just pass in the query directly
beforeEach(() => {
cy.task("cleanUpDB", "UPDATE SQL GOES HERE")
on("task", {
cleanUpDB(theQuery) { // name can be anything
...
Based on the documentation of the library, the below code should work.
const sqlQuery = 'UPDATE SQL GOES HERE';
beforeEach(() => {
cy.task('cleanUpDB', {
sqlQuery,
});
});
// Config file
on('task', {
cleanUpDB({ theQuery }) {
sql.on('error', (err) => {
// Error handling
});
sql
.connect(DBConfig)
.then((pool) => {
return pool.request.query(theQuery);
})
.then((result) => {
console.log(result);
})
.catch((error) => {
console.log(error);
});
},
});

Pushing data from React in array in MongoDB

I want to push a string in an array in a MongoDB document using React/NodeJS/MongoDB,
Here's my code in React
async function toggleLike() {
try {
const dataUser = await axios.post(
`http://localhost:5000/user/${props.auth.user.id}/add/moviesLiked/${props.match.params.id}`);
console.log("user ", dataUser);
forceUpdate();
} catch (error) {
console.log(error);
}
Here's my code in NodeJS
router.post("/user/:user/add/moviesLiked/:movie", function(req, res) {
console.log("in api function add");
mongo.connect(
url,
{
useNewUrlParser: true,
useUnifiedTopology: true
},
(err, client) => {
if (err) {
console.error(err);
return;
}
const db = client.db("ofilms-demo");
const collection = db.collection("users");
collection.update(
{ _id: req.params.user },
{ $addToSet: { moviesLiked: req.params.movie } }
);
console.log("req params user ", req.params.user);
console.log("req params movie ", req.params.movie);
client.close();
}
);
});
Here's the model of an user in Mongoose
const UserSchema = new Schema({
moviesLiked: Array,
moviesDisliked: Array,
});
All my console.log show the right thing, but I still don't have the data pushed in the array,
Can somebody help me ? Thank you,
collection.update is asynchronous, so you need to wait for it to finish executing before closing your connection to Mongo and returning a response to the client.
You can wait for the update operation to complete by either passing a call back to the update method or using the async/await javascript feature.
Passing a call back function:
router.post("/user/:user/add/moviesLiked/:movie", function (req, res) {
mongo.connect(
url,
{
useNewUrlParser: true,
useUnifiedTopology: true
},
(err, client) => {
if (err) {
console.error(err);
return;
}
const db = client.db("ofilms-demo");
const collection = db.collection("users");
collection.update(
{ _id: req.params.user },
{ $addToSet: { moviesLiked: req.params.movie } },
function (error, result) { // The callback function
if (error) {
// Handle the error and send a respone to the user
} else {
// Make use of the result and send a response to the user
}
client.close();
}
);
}
);
});
Using async/await:
// Add the async keyword before declaring the function
router.post("/user/:user/add/moviesLiked/:movie", async function (req, res) {
mongo.connect(
url,
{
useNewUrlParser: true,
useUnifiedTopology: true
},
(err, client) => {
if (err) {
console.error(err);
return;
}
const db = client.db("ofilms-demo");
const collection = db.collection("users");
try {
// Add the await keyword before the update call
await collection.update(
{ _id: req.params.user },
{ $addToSet: { moviesLiked: req.params.movie } },
);
// Send response to your client
} catch (err) {
// Handle any possible error
}
client.close();
console.log("req params user ", req.params.user);
console.log("req params movie ", req.params.movie);
}
);
});
After DB i/o operation is done you should send back the response to your client something like this:
use try-catch to get the error message without crashing the whole node server.
Don't forget to send back the response to client otherwise, the client-side will keep waiting for server response until it's timeout reached
Node.js
router.post("/user/:user/add/moviesLiked/:movie", async (req, res) =>{
console.log("in api function add");
mongo.connect(
url,
{
useNewUrlParser: true,
useUnifiedTopology: true
},
(err, client) => {
if (err) {
console.error(err);
res.status(500).send({"message":"error occured", err})
return;
}
try{
const db = client.db("ofilms-demo");
const collection = db.collection("users");
const response = await collection.update(
{ _id: req.params.user },
{ $addToSet: { moviesLiked: req.params.movie } }
);
console.log("req params user ", req.params.user);
console.log("req params movie ", req.params.movie);
//send back the response
res.status(200).send({response, "message":"your profile is successfully updated."})
client.close();
}catch(err){
//check what is the error in your Nodejs console (Not browser console)
console.log(err)
//send back response
res.status(500).send({"message":"error occured", err})
}
);
}
});
MongoDB is itself schema-less. you don't have to provide schema. if you want to provide your own schema I'd recommend using mongoose. & mongoose arrays

I'm trying to query MySQL as a promise, but I keep getting "unhandledpromiserejection error"

Please help I'm trying to deploy my app to App Engine/CloudSQL but I keep getting :
"UnhandledPromiserejectWarning": Cannot enqueue after fatal error..
I'm trying to query MySQL as promise, when I don't I handle the exception it works fine locally, but when I deploy it doesn't work.
How can I handle promise rejection, please Help Thanks
This is db.js
const db = require('./Mysql')
const query = (q, data) => {
return new Promise((resolve, reject) => {
db.query(q, data, (err, res) => (err ? reject(err) : resolve(res)))
})
.then(res => console.log(res))
.catch(err => console.error(err))
This is Mysql.js
{ SQL_SOCKET, SQL_USER, SQL_PASSWORD, SQL_DATABASE } = process.env
const db = mysql.createConnection({
socketPath: SQL_SOCKET,
user: SQL_USER,
password: SQL_PASSWORD,
database: SQL_DATABASE,
charset: 'utf8mb4',
})
module.exports = db
I remember having this problem a few years ago when I tried using the mysql module within an expressJS application and attempted to use async/await. The error could also come from querying on a connection in which a fatal error occured, see here. As such, best practices dictates that on queries, you open a connection, start a query transaction, commit the query and then release the connection afterwards -- allowing you to rollback whenever an error occurs. I do not see this process happening here so it could be a possibility.
In any case, I can provide you with an alternative, which is the method I ended up going with. Basically, I digressed promisifying query() myself and instead let node handle it.
An example of using query without transaction:
/backend/database/database.js
const mysql = require('mysql');
const db = require('../config/config').mysql;
const util = require('util');
const pool = mysql.createPool({
connectionLimit: require('../config/config').cLimit,
host: db.host,
user: db.user,
password: db.password,
database: db.database
});
pool.query = util.promisify(pool.query);
async function query(cmd) {
try {
let result = await pool.query(cmd);
return result;
} catch(error) {
console.error(error);
}
}
module.exports = {
query
};
Which can then be used in your models like such:
/backend/models/User.js
const db = require('../database/database');
async function getUserById(userId) {
const cmd = `SELECT * FROM Users WHERE userId = ${userId}`;
try {
let result = await db.query(cmd);
return result;
} catch(error) {
throw {
message: error
}
}
}
module.exports = {
getUserById
};
Which in turn, can be called from your route handler like such:
/backend/routes/users.js
const router = require('express').Router();
const User = require('../models/User');
router.get('/getUserById/:userId', async (req, res) => {
try {
let user = await User.getUserById(req.params.userId);
res.status(200).send({ user });
} catch(error) {
console.error(error);
res.status(400).send({ error: 'Unable to fetch user' });
}
});
module.exports = router;

Node MSSQL: trying to insert a row using a function

I'm writing a function which makes an insert using mssql every time it is called. The function received 2 params (action, user), which is the data inserted into the SQL Server table.
Function looks like this:
function saveActionToDB(action, user) {
if (config.Logging.DB.type == 'mssql') {
const dbOptions = {
user: config.Logging.DB.user,
password: config.Logging.DB.password,
server: config.Logging.DB.server,
database: config.Logging.DB.database,
options: {
encrypt: config.Logging.DB.encrypt
}
};
const database = require('mssql');
const emmitQuery = async () => {
try {
const pool = await database.connect(dbOptions);
const request = pool.request();
request.input('action', sql.VarChar, action);
request.input('user', sql.VarChar, user);
request.query('insert into actions (action, user) values (#action, #user)', (err, result)=>{
//if (err) combinedLogger.error(err);
});
}
catch (err) {
combinedLogger.error(err);
}
}
emmitQuery();
}
else if(config.Logging.DB.type == 'oracle') {
//oracle
}
}
However, I am getting a console error with "undefined", which means that the try is catching an error, however, the err it's undefined, so I can't figure out what the error is.
Can anyone help?

NodeJS MSSQL driver Passing data to a calling function

Working with an SQL Server in NodeJS, and I have confirmed that it's pulling the data, but I'm trying to pass the data back to a calling function.
Here's my call to the function that queries the database:
const dbq = require('./dbquery.js');
app.get('/:id', (req, res) => {
reqlog(`/${req.params.id}`);
var set = dbq.getPersonById(req.params.id);
console.log(set);
});
and here is the function inside dbquery.js:
qry.getPersonById = (id) => {
mssql.connect(sqlConfig).then(() => {
new mssql.Request().query(`select * from FNVPeople where IndivID=${id}`).then((record)=>{
console.log(record);
return record;
}).catch((err)=>{
console.log(err);
});
});
}
Should my call to the function look like this?
var dataset = await(dbq.getPersonById(req.params.id));
Because of Async nature.
Try for the following:
const dbq = require('./dbquery.js');
app.get('/:id', (req, res) => {
reqlog(`/${req.params.id}`);
dbq.getPersonById(req.params.id, function(err, res){
console.log(res);
});
});
qry.getPersonById = (id, callback) => {
mssql.connect(sqlConfig).then(() => {
new mssql.Request().query(`select * from FNVPeople where IndivID=${id}`).then((record)=>{
console.log(record);
callback(null, record);
}).catch((err)=>{
console.log(err);
});
});
}
You should return the promise to the client to deal with (note two additional returns):
qry.getPersonById = (id) => {
return mssql.connect(sqlConfig).then(() => {
return new mssql.Request().query(`...`)
.then((record)=>{
console.log(record);
return record;
})
.catch((err)=>{
console.log(err);
});
});
}
Then, the client deals with the promise
app.get('/:id', (req, res) => {
reqlog(`/${req.params.id}`);
dbq.getPersonById(req.params.id).then( set =>
console.log(set);
);
});
By rewriting promises to async/await you could even have
qry.getPersonById = async (id) => {
try {
await mssql.connect(sqlConfig)
var record = await new mssql.Request().query(`...`);
console.log(record);
return record;
}
catch (err) {
console.log(err);
}
}
and
app.get('/:id', async (req, res) => {
reqlog(`/${req.params.id}`);
var set = await dbq.getPersonById(req.params.id);
console.log(set);
});

Resources