I'm using the mssql module to connect to a sql server database using node. Bluebird has a feature that's similar to resource management in c#. It has a 'using' method to avoid having to use try/catch/finall to dispose of the resources. They have examples for pg and mysql, but they don't have an example for mssql which doesn't create a connection the same way as pg and mysql. Here's an example of how to use it:
using(getConnection(),
fs.readFileAsync("file.sql", "utf8"), function(connection, fileContents) {
return connection.query(fileContents);
}).then(function() {
console.log("query successful and connection closed");
});
But to be able to use this method, you need to create a connection method which describes how to close the connection. Here's an example for pg:
function getSqlConnection(connectionString) {
var close;
return pg.connectAsync(connectionString).spread(function(client, done) {
close = done;
return client;
}).disposer(function(client) {
if (close) close(client);
});
}
The problem I'm having with mssql module is that the connect method doesn't return a connection object like pg or even the mysql module. Has anyone been able to do this with mssql?
Update 1:
Here's how I made the transaction disposer:
function getTransaction(connection) {
return new Promise(function(resolve, reject) {
var tx = sql.Transaction(connection);
tx.beginAsync().then(function(err) {
if(err) {
tx = null;
return reject(err);
}
return resolve(tx);
});
}).disposer(function(tx, promise) {
if(promise.isFulfilled()) {
return tx.commitAsync();
}
else {
return tx.rollbackAsync();
}
});
}
It seems to be working, but not sure if this is efficient. Now I need to figure out how to catch errors on a query.
This is how I'm doing a transaction:
using(getConnection(), function(connection) {
return using(getTransaction(connection), function(tx) {
return query(queryString, tx).then(function() {
console.log('first query in transaction completed.');
console.log('starting second query in transaction.');
return query(anotherQueryString, tx);
});
});
});
If I tag a single catch to the outer 'using', will that catch all errors from the whole transaction?
Good question, mssql has really tricky API (constructors taking callbacks!) so this is good addition to the documentation.
var Promise = require("bluebird");
var sql = Promise.promisifyAll(require("mssql"));
global.using = Promise.using;
function getConnection(config) {
var connection;
return new Promise(function(resolve, reject)
connection = new sql.Connection(config, function(err) {
if (err) {
connection = null;
return reject(err);
}
resolve(connection);
});
}).disposer(function() {
if (connection) connection.close();
});
}
var config = {
user: '...',
password: '...',
server: 'localhost',
database: '...',
};
using(getConnection(config), function(connection) {
var request = new sql.Request(connection);
return request.queryAsync("select 1 as number").then(function(recordSet) {
console.log("got record set", recordSet);
return request.queryAsync("select 10 as number");
});
}).then(function(recordSet) {
console.log("got record set", recordSet);
})
To use the transaction, try implementing getTransaction like:
function getTransaction(connection) {
var tx = new sql.Transaction(connection);
return tx.beginAsync().thenReturn(tx).disposer(function(tx, promise) {
return promise.isFulfilled() ? tx.commitAsync() : tx.rollbackAsync();
});
}
And using it like:
using(getConnection(), function(connection) {
return using(getTransaction(connection), function(tx) {
var request = new sql.Request(tx);
return request.queryAsync("INSERT 1...").then(function() {
return request.queryAsync("INSERT 2...");
}).then(function() {
return request.queryAsync("INSERT 3...");
});
});
});
Error handling:
using(getConnection(), function(connection) {
return using(getTransaction(connection), function(tx) {
var request = new sql.Request(tx);
return request.queryAsync("INSERT...");
});
}).catch(sql.TransactionError, function(e) {
console.log("transaction failed", e);
}).catch(sql.ConnectionError, function(e) {
console.log("connection failed", e);
}).catch(sql.RequestError, function(e) {
console.log("invalid query", e);
});
Related
I'm very new to node.js so I think I'm missing something obvious here.
I'm simply trying to get a list of SQS queues using aws-sdk and return them from a module to be accessible to other code. list_queues is the function in question.
The code below works to an extent, I see a "success" log and a log of a string array of all my queues, however, the function does not return that array to the caller and I don't understand why.
const AWS = require('aws-sdk');
AWS.config.update({region: 'eu-west-1'});
var sqs;
var sts = new AWS.STS();
sts.assumeRole({
RoleArn: 'arn:aws:iam::xxxxx:role/UserRole',
RoleSessionName: 'NodeDeveloperRoleSession'
}, function(err, data) {
if (err) { // an error occurred
console.log('Cannot assume role :(');
console.log(err, err.stack);
} else { // successful response
console.log('Assumed role success :)');
AWS.config.update({
accessKeyId: data.Credentials.AccessKeyId,
secretAccessKey: data.Credentials.SecretAccessKey,
sessionToken: data.Credentials.SessionToken
});
sqs = new AWS.SQS({apiVersion: '2012-11-05'});
}
});
exports.list_queues = function() {
sqs.listQueues({}, function(err, data) {
if (err) {
console.log("Error", err);
} else {
console.log("success");
console.log(data.QueueUrls);
return data.QueueUrls;
}
});
}
Any help is appreciated
exports.list_queues = function() { // 2. but you actually want to return from this one
sqs.listQueues({}, function(err, data) { <-----------------
if (err) { |
console.log("Error", err); |
} else { |
console.log("success"); |
console.log(data.QueueUrls); |
return data.QueueUrls; // 1. you are returning from this one
}
});
}
there are two ways you can make it work
Promise based
exports.list_queues = function() {
return sqs.listQueues({}).promise().then((data) => data.QueueUrls);
}
// and in another file you would:
const {list_queues} = require('./list_queues.js');
list_queues.then((queues) => console.log(queues));
Callback based
exports.list_queues = function(cb) { // notice I added callback here
sqs.listQueues({}, function(err, data) {
if (err) {
console.log("Error", err);
} else {
console.log("success");
console.log(data.QueueUrls);
cb(data.QueueUrls);
}
});
}
// and in another file you would:
const {list_queues} = require('./list_queues.js');
list_queues(function(queues) {
console.log(queues);
});
I strongly recommend you to use promise based approach, since it's much more readable and you can make use of async/await on it, which is great.
I have a function that connects to a sql database, queries it, formats the results into an html table and returns the html variable:
function getData() {
return new Promise((resolve, reject) => {
var sql = require("mssql");
var dbConfig = {
server: "server",
database: "db",
user: "user",
password: "pw"
}
var conn = new sql.Connection(dbConfig);
var req = new sql.Request(conn);
conn.connect(function (err) {
if (err) {
console.log(err);
reject(err);
return;
}
req.query("SELECT * FROM table",
(err, recordset) => {
// Here we call the resolve/reject for the promise
try {
// If the results callback throws exception, it will be caught in
// the catch block
resolve(resultsCallback(err, recordset));
}
catch (e) {
reject(e);
}
}
);
conn.close();
});
})
}
function resultsCallback(err, recordset) {
var tableify = require('tableify');
if (err) {
console.log(err);
throw err;
}
else {
var html = tableify(recordset);
html = html.replace('<table>', '');
html = html.replace('</table>', '');
return html;
}
};
And I am calling it like this:
getData().then((data)=>{console.log("Table data:",data);})
.catch((error)=>{console.log("ERROR LOADING SQL:",error);})
However, for some reason the output from this is: Table Data: undefined
I am unsure why this would be happening like this. Did I return the data correctly?
i think your resultsCallback is unnecessarily tangled up with error handling
i tried to clean up your example with some modern flair, hope it helps you out
const sql = require("mssql")
const tableify = require("tableify")
/**
* FORMAT RESULTS
* - format sql records as html
* - returns a string of html
*/
function formatResults(records) {
return tableify(records)
.replace("<table>", "")
.replace("</table>", "")
}
/**
* GET DATA
* - query records from a database
* - returns a promised string of html
*/
async function getData({db, table}) {
// open the sql connection pool
const pool = await sql.connect(db)
// query the database and format the results
try {
const results = await pool.request()
.input("tablename", table)
.query(`SELECT * from #tablename`)
return formatResults(results)
}
// rethrow query errors
catch (error) {
error.message = `getData sql query error: ${error.message}`
throw error
}
// always close the connection
finally {
pool.close()
}
}
// USAGE EXAMPLE BELOW
;(async() => {
const data = await getData({
db: {
server: "server",
database: "db",
user: "user",
password: "pw"
},
table: "table"
})
console.log(data)
})().catch(error => console.error(error))
In my login form I have an option that allow me to select which database I want to connect, so I'm trying to create a function to do that.
In db config file I have a function named setDB that go to another function named makeConnection sending the dbname as parameter and handle the result of this function.
The problem is that second function doesn't return any result but a promise pending.
In this function I have 3 return that means error that will be handle in setDB function but this not work.
var mssql = require('mssql');
module.exports =
{
setDB: (req, res) =>
{
console.log('Prepare to connect to ' + req);
var result = makeConnection(req);
console.log(result);
if(result == 0)
{
res.send('Unknown database!');
}
else if(result == 1)
{
res.send('Error trying to connect!')
}
else if(result == 2)
{
res.send('Connection done!')
}
//return connection;
}
}
function makeConnection(dbname)
{
console.log('Start connection....');
return new Promise(function(resolve, reject) {
console.log('Database: '+ dbname);
var configs = {
Emp1: {
user: "us",
password: "pass",
server: "ip",
database: "Emp1"
},
Emp2: {
user: "us",
password: "pass",
server: "ip",
database: "Emp2"
}
};
var config = configs[dbname];
if(config == undefined)
{
return 0;
}
var connection = new mssql.Connection(config);
connection.connect(function(err)
{
if (err) {
console.log(err);
reject(err);
return 1;
} else {
resolve(connection);
console.log('Database Connected!');
return 2;
}
});
});
}
Console Print:
What is the better way to do this?
I want to do a single connection and not do a connection in each request... And for example it have to be prepare to be used by 2 user in different databases at same time.
Which is the better way to do that?
Thank you
makeConnection() returns a Promise. So naturally when you call:
var result = makeConnection(req);
console.log(result);
It will result in logging a pending promise. That's what is should do. Your function immediately returns a pending promise that will resolve once the async operations have returned. But you are not waiting for them before trying to log the results. You need to use the promise's then() function to get the results when they are ready:
makeConnection(req)
.then(result => {
console.log(result);
if(result == 0)
{
res.send('Unknown database!');
}
else if(result == 1)
{
res.send('Error trying to connect!')
}
else if(result == 2)
{
res.send('Connection done!')
}
}
I am playing with elasticsearch.js from the browser. I would like to ping elasticsearch, wait for the request to complete and then return the result of the connection. But right now it is happening asynchronously, and returning undefined even when the connection is ok. I have code like this:
var connectionOK = false;
function createElasticsearchClient(hostAddress) {
var client = new $.es.Client({
hosts: hostAddress
});
return client;
}
function checkElasticsearchConnection(client) {
$.when(pingElasticsearch(client)).done(function () {
return connectionOK;
});
}
function pingElasticsearch(client) {
console.log("ELASTICSEARCH: Trying to ping es");
client.ping({
requestTimeout: 30000,
// undocumented params are appended to the query string
hello: "elasticsearch"
}, function (error) {
if (error) {
console.error('ELASTICSEARCH: Cluster is down!');
connectionOK = false;
console.log("INSIDE: " + connectionOK);
} else {
console.log('ELASTICSEARCH: OK');
connectionOK = true;
console.log("INSIDE: " + connectionOK);
}
});
}
and how it is used:
var esClient = createElasticsearchClient("exampleserver.com:9200");
var esCanConnect = (checkElasticsearchConnection(esClient));
You're mixing asynchronous functions with synchronous functions. You could go with this approach instead:
function createElasticsearchClient(hostAddress, callback) {
var client = new $.es.Client({
hosts: hostAddress
});
return callback(client);
}
function pingElasticsearch(client, callback) {
console.log("ELASTICSEARCH: Trying to ping es");
client.ping({
requestTimeout: 30000,
// undocumented params are appended to the query string
hello: "elasticsearch"
}, function (error) {
if (error) {
return callback('ELASTICSEARCH: Cluster is down!');
} else {
return callback(null);
}
});
}
And then run
createElasticsearchClient("exampleserver.com:9200", function(esClient) {
pingElasticsearch(esClient, function(err) {
if (err) console.log(err);
else {
//Everything is ok
console.log('All good');
}
});
});
I am trying to create REST service using Nodejs and Mysql.
his is my code:
var sqlDb = require("mysql");
var settings = require("../settings");
exports.executeSql = function (sql, callback) {
var conn = sqlDb.createConnection(settings.dbConfig);
conn.connect()
.then(function () {
var req = new sqlDb.Request(conn);
req.query(sql)
.then(function (recordset) {
callback(recordset);
})
.catch(function (err) {
console.log(err);
callback(null, err);
});
})
.catch(function (err) {
console.log(err);
callback(null, err);
});
};
But I have an error
.then(function(){
^
TypeError: cannot read property 'then' of undefined
Can anybody help me to solve this problem?
The correct function is createConnection. Read the docs please:
https://www.npmjs.com/package/mysql#introduction
In the documentation, the connect method is not returning a promise if you look at the documentation this can work as callback method:
var connection = mysql.createConnection({
host : 'example.org',
user : 'bob',
password : 'secret'
});
//** Look here...
connection.connect(function(err) {
if (err) {
console.error('error connecting: ' + err.stack);
return;
}
console.log('connected as id ' + connection.threadId);
});
As a good alternative, you can use the package promise-mysql that is using bluebird and you can locate here (which I strongly suggest to use) or you can wrap it up in a promise like using bluebird like:
async function connect(connection) {
return new Promise((resolve, reject) => {
connection.connect((err) => {
return err ? reject(err) : resolve(connection);
})
});
}