Query works but cant retrieve the data - sql

I am new to Node.js (3 days total experience). I am using Node.js and the tedious package to query a database (azure SQL). I use the example as explained here: https://learn.microsoft.com/en-us/azure/azure-sql/database/connect-query-nodejs?tabs=macos
const connection = new Connection(config);
// Attempt to connect and execute queries if connection goes through
connection.on("connect", err => {
if (err) {
console.error(err.message);
} else {
console.log("Reading rows from the Table...");
// Read all rows from table
const request = new Request(
"SELECT * FROM clients",
(err, rowCount, columns) => {
if (err) {
console.error(err.message);
} else {
console.log(`${rowCount} row(s) returned`);
}
}
);
request.on("row", columns => {
columns.forEach(column => {
console.log("%s\t%s", column.metadata.colName, column.value);
});
});
connection.execSql(request);
}
});
I have two issues:
I do not know how to get the queried data into an object and
If I run the script it does print the items to the console, but it doesn't close the connection after it has done so. If I add a connection.close() at the bottom, it will close the connection before its done. I get the feeling that node.js executes everything at the same time (I am used to Python..).
Update
I found a way to close the connection, to my understanding the request object has several "events" that are predefined by the library. It seems I need to add the event "done" through request.on('done', ...) in order to make sure that it can even BE done. My updated code looks like this:
var connection = new Connection(config);
connection.connect(function(err) {
// If no error, then good to go...
executeStatement();
}
);
connection.on('debug', function(text) {
//remove commenting below to get full debugging.
//console.log(text);
}
);
function executeStatement() {
request = new Request("SELECT * FROM clients", function(err, rowCount) {
if (err) {
console.log(err);
} else {
console.log(rowCount + ' rows');
}
connection.close();
});
request.on('row', function(rows) {
_.forEach(rows, function(value, collection){
console.log(value)
console.log(value.value);
console.log(value.metadata.colName)
console.log(collection)
})
});
request.on('done', function(rowCount, more) {
console.log(rowCount + ' rows returned');
});
// In SQL Server 2000 you may need: connection.execSqlBatch(request);
connection.execSql(request);
}
Anyways, your help would be much appreciated!
Regards
Pieter

The package tedious is synchronous package, it uses the callback to return results. So when we call connection.close(), it will disable connection and stop the callback function. If will want to close the connection, I suggest you use async package to implement it.
For example
const { Connection, Request } = require("tedious");
const async = require("async");
const config = {
authentication: {
options: {
userName: "username", // update me
password: "password", // update me
},
type: "default",
},
server: "your_server.database.windows.net", // update me
options: {
database: "your_database", //update me
encrypt: true,
validateBulkLoadParameters: true,
},
};
const connection = new Connection(config);
let results=[]
function queryDatabase(callback) {
console.log("Reading rows from the Table...");
// Read all rows from table
const request = new Request("SELECT * FROM Person", (err, rowCount) => {
if (err) {
callback(err);
} else {
console.log(`${rowCount} row(s) returned`);
callback(null);
}
});
request.on("row", (columns) => {
let result={}
columns.forEach((column) => {
result[column.metadata.colName]=column.value
console.log("%s\t%s", column.metadata.colName, column.value);
});
// save result into an array
results.push(result)
});
connection.execSql(request);
}
function Complete(err, result) {
if (err) {
callback(err);
} else {
connection.close();
console.log("close connection");
}
}
connection.on("connect", function (err) {
if (err) {
console.log(err);
} else {
console.log("Connected");
// Execute all functions in the array serially
async.waterfall([queryDatabase], Complete);
}
});
connection.connect();
Besides, you also can use the package mssql. It supports asynchronous methods and depends on package tedious. We can directly call close after querying.
For example
const mssql = require("mssql");
const config = {
user: "username",
password: "password",
server: "your_server.database.windows.net",
database: "your_database",
options: {
encrypt: true,
enableArithAbort: true,
},
};
let pool = new mssql.ConnectionPool(config);
async function query() {
try {
await pool.connect();
const request = pool.request();
const result = await request.query("SELECT * FROM Person");
console.dir(result.recordset);
await pool.close();
console.log(pool.connected);
} catch (error) {
throw error;
}
}
query().catch((err) => {
throw err;
});

You can custom a class first and declare an Array to save ojects such as:
let sales = new Array();
class SalesLT{
constructor(catagryName,productName){
this.catagryName = catagryName;
this.productName = productName;
}
Here my sql statement returns 2 properties, so every time the loop takes out two elements from the ColumnValue[].
request.on("row", columns => {
for(let i=0; i<columns.length; i=i+2){
let sale = new SalesLT(columns[i].value,columns[i+1].value);
sales.push(sale);
}
sales.forEach( item => {
console.log("%s\t%s",item.catagryName, item.productName)
})
});
The code is as follows:
const { Connection, Request } = require("tedious");
let sales = new Array();
class SalesLT{
constructor(catagryName,productName){
this.catagryName = catagryName;
this.productName = productName;
}
}
// Create connection to database
const config = {
authentication: {
options: {
userName: "<***>", // update me
password: "<***>" // update me
},
type: "default"
},
server: "<****>.database.windows.net", // update me
options: {
database: "<***>", //update me
encrypt: true
}
};
const connection = new Connection(config);
// Attempt to connect and execute queries if connection goes through
connection.on ("connect", err => {
if (err) {
console.error(err.message);
} else {
queryDatabase();
}
});
function queryDatabase() {
console.log("Reading rows from the Table...");
// Read all rows from table
const request = new Request(
`SELECT TOP 2 pc.Name as CategoryName,
p.name as ProductName
FROM [SalesLT].[ProductCategory] pc
JOIN [SalesLT].[Product] p ON pc.productcategoryid = p.productcategoryid`,
(err, rowCount) => {
if (err) {
console.error(err.message);
} else {
console.log(`${rowCount} row(s) returned`);
}
connection.close();
}
);
request.on("row", columns => {
for(let i=0; i<columns.length; i=i+2){
let sale = new SalesLT(columns[i].value,columns[i+1].value);
sales.push(sale);
}
sales.forEach( item => {
console.log("%s\t%s",item.catagryName, item.productName)
})
});
connection.execSql(request);
}

this article should help you, to solve all the issues you are facing...which were the same I had when I started using Node :)
https://devblogs.microsoft.com/azure-sql/promises-node-tedious-azure-sql-oh-my/

Related

Node.js wait("await") for SQL database query before proceeding

I have spent a lot of time reading up on this but I simply don't get how to solve it.
I have an application that uses a token that is stored in a SQL database. I need that token before the application can proceed.
I'm trying to solve it with "await" but it doesn't work. The SQL query result is still retrieved "too late".
const pool = mysql.createPool({
user : 'xxxx', // e.g. 'my-db-user'
password : "xxxx", // e.g. 'my-db-password'
database : "xxxx", // e.g. 'my-database'
// If connecting via localhost, specify the ip
host : "xxxx"
// If connecting via unix domain socket, specify the path
//socketPath : `/cloudsql/xxxx`,
});
const isAuthorized = async (userId) => {
let query = "SELECT * FROM auth WHERE id = 2";
await pool.query(query, (error,results) => {
if (!results[0]) {
console.log("No results");
return
} else {
tokenyay=results[0].refreshtoken;
console.log("results: "+results[0].refreshtoken);
return results[0].refreshtoken;
}
});
await console.log("tokenyay: "+tokenyay);
if (tokenyay != null && tokenyay != '') {
refreshTokenStore[userId] = tokenyay;
}
console.log(userId);
console.log(refreshTokenStore[userId] ? true : false);
return refreshTokenStore[userId] ? true : false;
};
I don't know what your pool is, but judging from the callback, we can see that pool.query() method is not await-able.
You can manually create a Promise for it, though, which is await-able, for example
await new Promise((resolve, reject) => {
pool.query(query, (error, results) => {
if (error) reject(error);
if (!results[0]) {
console.log("No results");
resolve(); // give `undefined` to the `await...` and make it stop waiting
return;
} else {
tokenyay = results[0].refreshtoken;
console.log("results: " + results[0].refreshtoken);
resolve(results[0].refreshtoken);
}
})
});
Edit:
However, since the result of await is obtained from the value passed to the resolve, we don't need to assign tokenyay inside of the callback.
We can use tokenyay = await... instead.
tokenyay = await new Promise((resolve, reject) => {
pool.query(query, (error, results) => {
if (error) reject(error);
if (!results[0]) {
console.log("No results");
resolve(); // give `undefined` to the `await...` and make it stop waiting
return;
} else {
console.log("results: " + results[0].refreshtoken);
resolve(results[0].refreshtoken);
}
})
});

Return Values from a callback SQL function in node.js

Currently I'm working on a node.js application, with a register function. For this function I need to check a username is already taken or not. Unfortunately the SQL module in node just accepts a callback function from which I cannot send any booleans back.
Here is some code from my controller module:
async function createUser(req, res) {
try {
const salt = await bcrypt.genSalt(); //standard ist 10
const hashedPassword = await bcrypt.hash(req.body.password, salt);
const newUser = {
userName: req.body.username,
userPassword: hashedPassword
};
const userExists = model.checkIfUserExists(newUser.userName);
if (userExists == false){
// create new user
} else {
// Send json back "user already exists
}
res.status(201).json(newUser);
} catch {
res.status(500);
}
}
And here is the code of the model:
function checkIfUserExists(Username){
console.log("Checking if user exists");
let sql = "select * from users where user_name = ?";
db_conn.query(sql, Username, (err, result) => {
if (err){
throw err;
}
console.log(result);
if (result.length > 0){
return true;
} else {
return false;
}
});
}
Unfortunately the "checkIfUserExists" method never returns back a true or false which leads to the "userExists " variable to be null.
I'd like to know how to do return the bollean there or how to solve the problem in a more elegant way.
Please help me to fix this code. Thanks :)
You can either pass a callback to checkIfUserExists or use promises. If I were you, and since you are already using async/await, I would make your return of checkIfUserExists be a promise. So...your code could become
function checkIfUserExists(Username) {
return new Promise((resolve,reject) => {
console.log("Checking if user exists");
let sql = "select * from users where user_name = ?";
db_conn.query(sql, Username, (err, result) => {
if (err) {
throw err;
}
console.log(result);
if (result.length > 0) {
resolve()
} else {
reject()
}
});
})
}
Then, your code that calls this function would be:
async function createUser(req, res) {
try {
const salt = await bcrypt.genSalt(); //standard ist 10
const hashedPassword = await bcrypt.hash(req.body.password, salt);
const newUser = {
userName: req.body.username,
userPassword: hashedPassword
};
await model.checkIfUserExists(newUser.userName).catch(() => {
// Send json back "user already exists
});
// create user
res.status(201).json(newUser);
} catch {
res.status(500);
}
}
First check your catch statement and also add await before model.checkIfUserExists(newUser.userName)
async function createUser(req, res) {
try {
const salt = await bcrypt.genSalt(); //standard ist 10
const hashedPassword = await bcrypt.hash(req.body.password, salt);
const newUser = {
userName: req.body.username,
userPassword: hashedPassword
};
const userExists = await model.checkIfUserExists(newUser.userName);
if (userExists == false){
// create new user
} else {
// Send json back "user already exists
}
res.status(201).json(newUser);
} catch(ex) {
res.status(500);
}
}
return promise from this function:
function checkIfUserExists(Username){
return new Promise((resolve, reject) => {
console.log("Checking if user exists");
let sql = "select * from users where user_name = ?";
db_conn.query(sql, Username, (err, result) => {
if (err){
return reject(err);
}
console.log(result);
if (result.length > 0){
return resolve(true);
} else {
return resolve(false);
}
});
})
}

SQL database not saving

I'm relatively new to SQLite and I've been trying to modify a project made by people over at glitch. The app is supposed to save text to a database, and my project link is this. Although the logs don't show any errors. I can't get any data to save no matter what I do. (I changed the database file saving location from .data/sqlite.db to /data.sqlite.db). I assume this is some small issue I'm just new about, but I can't find anything about it on the internet.
app.use(express.static("public"));
// init sqlite db
const dbFile = "./data/sqlite.db";
const exists = fs.existsSync(dbFile);
const sqlite3 = require("sqlite3").verbose();
const db = new sqlite3.Database(dbFile);
// if ./.data/sqlite.db does not exist, create it, otherwise print records to console
db.serialize(() => {
if (!exists) {
db.run(
"CREATE TABLE Dreams (id INTEGER PRIMARY KEY AUTOINCREMENT, dream TEXT)"
);
console.log("New table Dreams created!");
// insert default dreams
db.serialize(() => {
db.run(
'INSERT INTO Dreams (dream) VALUES ("test"), ("hi sam"), ("fortnite gaming chair 3d")'
);
});
} else {
console.log('Database "Dreams" ready to go!');
db.each("SELECT * from Dreams", (err, row) => {
if (row) {
console.log(`record: ${row.dream}`);
}
});
}
});
// http://expressjs.com/en/starter/basic-routing.html
app.get("/", (request, response) => {
response.sendFile(`${__dirname}/views/index.html`);
});
// endpoint to get all the dreams in the database
app.get("/getDreams", (request, response) => {
db.all("SELECT * from Dreams", (err, rows) => {
response.send(JSON.stringify(rows));
});
});
// endpoint to add a dream to the database
app.post("/addDream", (request, response) => {
console.log(`add to dreams ${request.body.dream}`);
// DISALLOW_WRITE is an ENV variable that gets reset for new projects
// so they can write to the database
if (!process.env.DISALLOW_WRITE) {
const cleansedDream = request.body.dream;
db.run(`INSERT INTO Dreams (dream) VALUES (?)`, cleansedDream, error => {
if (error) {
response.send({ message: "error!" });
} else {
response.send({ message: cleansedDream+"success" });
}
});
}
});
// endpoint to clear dreams from the database
app.get("/clearDreams", (request, response) => {
// DISALLOW_WRITE is an ENV variable that gets reset for new projects so you can write to the database
if (!process.env.DISALLOW_WRITE) {
db.each(
"SELECT * from Dreams",
(err, row) => {
console.log("row", row);
db.run(`DELETE FROM Dreams WHERE ID=?`, row.id, error => {
if (row) {
console.log(`deleted row ${row.id}`);
}
});
},
err => {
if (err) {
response.send({ message: "error!" });
} else {
response.send({ message: "success" });
}
}
);
}
});
// listen for requests :)
var listener = app.listen(process.env.PORT, () => {
console.log(`Your app is listening on port ${listener.address().port}`);
});

passing Tedious connection as parameter

I am trying to use a simple suite of functions built utilizing the Tedious library to access a Microsoft SQL Server. Here is my "tools" file:
'use strict';
const tedious = require('tedious');
const q = require('q');
var Connection = tedious.Connection;
var Request = tedious.Request;
module.exports = {
connectSQL : function(config) {
var connection = new Connection(config);
connection.on('connect', function(err) {
if (err) {
console.log('FAIL ON CONNECT');
console.log(err);
} else {
try {
/* ----- */
return connection;
} catch (err) {
console.log(err);
return;
}
}
});
connection.on('error', function(err) {
if (err) {
console.log('FAIL ON ERROR');
console.log(err);
} else {
console.log("Error called with no err object.");
}
});
},
executeSQL: function(connection, requestString) {
var results = [];
var request = new Request( requestString , function(err, data) {
if (err) {
console.log(err);
} else {
console.log( data );
}
});
request.on('row', function(row) {
//console.log(row);
results.push( row );
});
request.on('requestCompleted', function(){
console.log('Finished');
return results;
});
connection.execSql(request);
}
}
I call these functions as follows in my server file.
const sqlTools = require('./sqlTools.js');
var connection = sqlTools.connectSQL(config);
sqlTools.executeSQL(connection, "select * from dbo.test");
However, I get the error "TypeError: Cannot read property 'execSql' of undefined", even if I make the program sleep for 10 seconds before calling my function sqlTools.executeSQL (obviously not ideal).
I was able to get this to work by calling the request within the sqlTools.connectSQL function (at the "/* ----- */"), but I want to re-use the Tedious connection to make multiple calls. Any suggestions? Thanks!
~~~~~~~EDIT~~~~~~~~~~
With help from akinjide I was able to implement callbacks that allow me to make a single call to my SQL database. However, I am struggling to implement promises to make subsequent calls. I changed my "tools" file as such:
'use strict';
const tedious = require('tedious');
const q = require('q');
var Connection = tedious.Connection;
var Request = tedious.Request;
module.exports = {
connectSQL: function(config) {
var deferred = q.defer();
var connection = new Connection(config);
connection.on('connect', function(err) {
if (err) {
deferred.reject( err );
} else {
deferred.resolve( connection );
}
});
connection.on('error', function(err) {
deferred.reject(err);
});
return deferred.promise;
},
executeSQL: function(connection, requestString, callback) {
var results = [];
const request = new Request(requestString, function(err) {
callback(err);
});
request.on('row', function(row) {
results.push(row);
});
request.on('requestCompleted', function() {
console.log('request completed!');
callback(null, results);
});
connection.execSql(request);
}
}
and I call this code like this...
var promise = sqlTools.connectSQL(config);
promise.then(function (connection) {
sqlTools.executeSQL(connection, "select * from dbo.test", function(err, results) {
if (err) {
console.log(err);
}
console.log(results);
});
}).catch(function (err) {
console.log(err);
}).then(function (connection) {
sqlTools.executeSQL(connection, "select * from dbo.test2", function(err, results) {
if (err) {
console.log(err);
}
console.log(results);
});
}).catch(function(err) {
console.log(err);
});
This returns the first call's results correctly, but unfortunately returns this error "TypeError: Cannot read property 'execSql' of undefined" for the second call as it is not recognizing the connection the second time around. Any suggestions?
A better approach would be to pass a node.js callback style function as an argument to connectSQL.
return keyword won't work within an asynchronous program.
'use strict';
const tedious = require('tedious');
const Connection = tedious.Connection;
const Request = tedious.Request;
module.exports = {
connectSQL: function(config, callback) {
const connection = new Connection(config);
connection.on('connect', function(err) {
if (err) {
callback(err);
} else {
callback(null, connection);
}
});
connection.on('error', function (err) {
callback(err);
});
},
executeSQL: function(connection, requestString, callback) {
let results = [];
const request = new Request(requestString, function(err) {
callback(err);
});
request.on('row', function(row) {
results.push(row);
});
request.on('requestCompleted', function(){
console.log('Finished');
callback(null, results);
});
connection.execSql(request);
}
}
Then you can require, use sqlTools.connectSQL passing two parameters config and function(err, connection) {}
const sqlTools = require('./sqlTools');
sqlTools.connectSQL(config, function(err, connection) {
if (err) {
console.log('FAIL ON CONNECT');
console.log(err);
}
sqlTools.executeSQL(connection, "select * from dbo.test", function (err, results) {
if (err) {
console.log(err);
}
console.log(results);
});
});

Node js: mssql [ConnectionError: Connection is closed.] name: 'ConnectionError', message: 'Connection is closed.', code: 'ECONNCLOSED'

i am getting error in npm mssql 3.0.0 with sqlserver 2012
i am creating single page application where i used restful using express .
there are 4 method which executing the query and returning the data to response.
for each method i am opening the connection and closing the connection.
but when savedquery is calling then connection close error occurs.
each method code is similar to savedquery method (copy pasted code only queries are changed) but they are executing savedquery is not executing
{ [ConnectionError: Connection is closed.]
name: 'ConnectionError',
message: 'Connection is closed.',
code: 'ECONNCLOSED' }
var savedquery=function(req,res){
dbConfig= {
user: 'XXX',
password: 'XXXXXXXXXX',
server: 'localhost', // You can use 'localhost\\instance' to connect to named instance
database: 'DEMO_ODS',
options: {
encrypt: true
}
};
sql.connect(dbConfig).then(function (err) {
var sqlrequest = new sql.Request();
sqlrequest.query("SELECT * from SavedQuery").then(function (recordset) {
sql.close(function (value) {
console.log("connection6 closed");
});
return res.status(200).send(recordset);
}).catch(function (err) {
console.log(err);
});
}).catch(function (err) {
console.log(err);
});
};
}
I know it is an old questionm but this answer is for the others who are facing the same isue. I had the same problem, What I did is, used promises as below.
function getData() {
try {
sqlInstance.connect(setUp)
.then(function () {
// Function to retrieve all the data - Start
new sqlInstance.Request()
.query("select * from Course")
.then(function (dbData) {
if (dbData == null || dbData.length === 0)
return;
console.dir('All the courses');
console.dir(dbData);
})
.catch(function (error) {
console.dir(error);
});
// Function to retrieve all the data - End
// To retrieve specicfic data - Start
var value = 1;
new sqlInstance.Request()
.input("param", sqlInstance.Int, value)
.query("select * from Course where CourseID = #param")
.then(function (dbData) {
if (dbData == null || dbData.length === 0)
return;
console.dir('Course with ID = 1');
console.dir(dbData);
})
.catch(function (error) {
console.dir(error);
});
// To retrieve specicfic data - End
}).catch(function (error) {
console.dir(error);
});
} catch (error) {
console.dir(error);
}
}
This solved my issue. You can find the fix here.
You should remove
options: {
encrypt: true
}
from your dbConfig
I just use promise to handle concurrent request:
const executeQuery = function (res, query, dbName) {
dbConfig = {
user: "********",
password: "********",
server: "*******",
database: dbName
}
sql.connect(dbConfig).then(pool => {
return pool.request().query(query)
}).then(result => {
res.send(result);
}).catch(err => {
res.send(err);
});
}
Hope it's help someone.