Node.js and MSSQL stored procedures - sql

What would be the suggested way (if even possible) to call MSSQL SP from Node.js. The documentation https://www.npmjs.org/package/mssql is great but there is no mention of SP (stored procedure) anywhere.

The linked document does actually mention stored procedures:
var request = new sql.Request(connection);
request.input('input_parameter', sql.Int, 10);
request.output('output_parameter', sql.VarChar(50));
request.execute('procedure_name', function(err, recordsets, returnValue) {
// ... error checks
console.dir(recordsets);
});
Not sure it's wise to answer this question, but it might be valueable for future readers/googlers.

It's getting better with ES6/7 additions to JS. This is how you can do it with async/await:
async function getDataFromProcedure(dbConfig, procedureName) {
try {
await sql.connect(dbConfig);
const request = new sql.Request();
recordsets = await request.execute(procedureName);
return recordsets[0];
} catch (error) {
// handle error here
}
};

Alternate async/await syntax. I like the .then() format.
return await this.sqlConnectionPool.connect().then(async (pool) => {
return await pool
.request()
.input("UserID", sql.Int, id)
.execute("spADF_User_Get")
.then((result) => {
if (result.recordset && result.recordset.length === 1) {
return result.recordset[0];
} else {
//Something bad happened
}
});
});

Related

multiple SQL queries as one request on sequelize js

Is there a way for sequelize to send multiple queries as one request? Or does sequelize already does that?
Below is my code:
const transaction = await db.transaction();
try {
await Resource.bulkCreate(resourcesTable);
await postContacts(contactsTable, locationsTable);
await postResourceTag(resourceTagTable);
await transaction.commit();
} catch (err) {
logger.error(err);
await transaction.rollback();
throw new HttpError(500, "Database Error"); // placeholder until validation setup
}
const postContacts = async ({ contactsTable, locationsTable }) => {
if (contactsTable?.length) {
await Contact.bulkCreate(contactsTable);
if (locationsTable?.length) {
await Location.bulkCreate(locationsTable);
}
}
};
const postResourceTag = async (resourceTagTable) => {
if (resourceTagTable?.length) {
await Resource_Tag.bulkCreate(resourceTagTable);
}
};
Currently, I believe the bulkCreates are sent as separate requests? I'd prefer if they were sent as one single request like how it'd be if the SQL strings was concatenated and then sent.
Based on this answer you can use an array of promises and then handle them all in one point, is that what you might be looking for?

react-native-community/asyncStorage removeItem causes program to behave weirdly

I have this little code snippet executed during the user logout.
async function logoutAction(props) {
removeUser();
props.logoutUser();
}
The function inside removeUser() is as :
export const removeUser = async () => {
try {
await AsyncStorage.removeItem(Constant.storage.user_data);
await AsyncStorage.removeItem(Constant.storage.token);
await AsyncStorage.removeItem(Constant.storage.notification_token);
return true;
} catch (exception) {
return false;
}
}
This clears user related data from local storage.
Similarly, props.logoutUser() is a reference call to reducer which sets loggedIn status to false.
I'm having this issue that if the removeUser() function is called once, the axios http requests do not enter the interceptors anymore and every request catches an error 'undefined'. If this method is removed though, everything works fine.
I can get it to working state then by removing the interceptors once, performing a request and then adding the interceptors again, which I found after hours of here and there.
My interceptors are:
export const requestInterceptor = axios.interceptors.request.use(
async config => {
const token = await getToken();
if (token != '') {
config.headers.Authorization = token;
}
console.log('axios request', config);
return config;
},
error => {
// console.warn('on request error')
return Promise.reject(error);
},
);
export const responseInterceptor = axios.interceptors.response.use(
function(response) {
console.log('axios response', response);
// console.warn('on response success', response.status)
return response;
},
async function(error) {
if (error.response.status === 401) {
//logout user
return;
}
return Promise.reject(error);
},
);
I am using the #react-native-community/AsyncStorage package for maintaining local storage. I suspect that the issue might be in the removeItem method but I'm unsure as the official docs don't contain the removeItem method, or in the interceptor which doesn't seem faulty to me anyways.
What am I doing wrong here?? Please show me some light..
Or maybe try add a await before removeUser(); ?
async function logoutAction(props) {
await removeUser();
props.logoutUser();
}
The issue was quite silly and did not even concern AsyncStorage or removeItem and as Matt Aft pointed out in the comment, it was due to the call for token in the interceptor after it had been removed while logging out. So, replacing
const token = await getToken();
if (token != '') {
config.headers.Authorization = token;
}
by
await getToken()
.then(token => {
config.headers.Authorization = token;
})
.catch(_ => {
console.log('no token');
});
in the interceptor and returning promise from the getToken method did the thing.
Thanks to Matt and 高鵬翔.

How to prevent multi beginTransactions in Realmjs?

I create a function to handle transaction, then I call it to multi places. I got crash when another transaction not yet complete when I open new transaction.
Here my code:
const RealmMakeTransaction = async (action) => {
try {
realm.GetInstance().beginTransaction();
let response = await action();
realm.GetInstance().commitTransaction();
return response;
} catch (e) {
realm.GetInstance().cancelTransaction();
}
};
You can easily check if realm is already in transaction or not before calling beginTransaction() by calling realm.GetInstance().isInTransaction
Your code will look like :
const RealmMakeTransaction = async (action) => {
//use single instance
let realm = realm.GetInstance();
try {
if( realm.isInTransaction)
realm.cancelTransaction();
realm.beginTransaction();
let response = await action();
realm.commitTransaction();
return response;
} catch (e) {
realm.cancelTransaction();
realm.close();
}
};

Redis mocha Test case issue

I have one file call cache.js
var redisCache = redis.createClient(port, name);
redisCache.on("error", function(err) {
logger.error("Error connecting to redis", err);
});
exports.setExp = function(key, timeLeft, data){
redisCache.set(key, JSON.stringify(data), function (err, reply) {
console.log("error "+err);
console.log("reply "+reply);
if(err) {
console.log("error "+err.command + err.code);
logger.info("This errror on set key related to node_redis");
}
if(reply == 'OK') {
redisCache.expire(key, timeLeft, function (err, reply) {
if(err) {
logger.info("This errror on expire key related to node_redis");
}
if(reply === 1) {
logger.info(key+" key expire time set as "+timeLeft+" successfully!");
}
});
}
});
}
Now I want to write the test case for the above setExp function but some how the node_redis aways return me the err as null and reply as OK
below is my test case.
var cache = require(path.join(__dirname,'..','/cache'));
describe('cache', function () {
it('Cache #setExp() ', function (done) {
var result = cache.setExp(undefined, 0, []);
assert.equal('OK', results);
done()
})
})
IF I change the it should follow the below error I mention as per the node_redis test case
var result = cache.setExp('foo', 10, []);
it should return me the error called ERR wrong number of arguments for 'set' command
var result = cache.setExp(undefined, 0, []);
It should accept the below error log as
assert.equal(err.command, 'SET');
Please suggest me right way to achieve this.
Your thinking seems to be almost completely wrong here.
First of all, you're writing and using setExp as if it's a synchronous operation, but it isn't. It will return before the request is made to redis. It also never returns anything, so even if it was synchronous, result in your tests will always be undefined.
You need to redesign setExp as an asynchronous operation, either by using the async keyword, returning a promise, or having it accept a callback function.
Second of all, if you want to set an expiration on a Redis key, you should set it when you set the key itself, instead of setting the key with no expiration and then trying to add the expiration later. Otherwise you run the risk of the expiration setting failing, and then winding up with an orphaned key that never expires.
Here's an example, using node's util.promisify to as described in the node_redis docs:
var redis = require('redis');
var {promisify} = require('util');
var redisCache = redis.createClient(port, name);
redisCache.on("error", function(err) {
logger.error("Error connecting to redis", err);
});
var set = promisify(redisCache.set).bind(redisCache);
exports.setExp = function(key, timeLeft, data){
return set(key, JSON.stringify(data), 'EX', timeLeft.toString(10))
.then((reply) => {
if (reply !== 'OK') throw new Error(reply);
return reply;
});
};
In your tests you'd do something like this:
var cache = require('../cache');
describe('cache', function () {
it('Cache #setExp() ', function () {
let key = 'some key';
let timeLeft = 12345;
let data = { foo: 'bar' };
return cache.setExp(key, timeLeft, data)
.then((result) => {
assert.equal('OK', result);
});
});
});
Also, results and result are not the same thing. In your test case, there is no variable called results.
Oh, and don't do this:
var cache = require(path.join(__dirname,'..','/cache'));
require already supports paths relative to __dirname. Just do this:
var cache = require('../cache');

Node SQL Server mssql streaming

I am new to node and am working with mssql in order to connection to SQL Server. would anyone be able to assist in giving me a fuller example of mssql streaming. I find the git example vague and don't know where to start. Any assistance would be much appreciated.
var sql = require('mssql');
var config = {
user: '...',
password: '...',
server: 'localhost', // You can use 'localhost\\instance' to connect to named instance
database: '...',
stream: true,
options: {// Use this if you're on Windows Azure
}
}
sql.connect(config, function(err) {
var request = new sql.Request();
request.stream = true; // You can set streaming differently for each request
request.query('select * from verylargetable'); // or request.execute(procedure);
request.on('recordset', function(columns) {
// Emitted once for each recordset in a query
});
request.on('row', function(row) {
// Emitted for each row in a recordset
});
request.on('error', function(err) {
// May be emitted multiple times
});
request.on('done', function(returnValue) {
// Always emitted as the last one
});
});
I'm going to Holy Necro this post because I ran into the same problem today and would like to leave something that might help future me.
According to ExpressJs documentation, the proper way to stream any large set of data is to write it to the response, flush the response occasionally and then when done, end the response.
mssql on NPM states that there are a few events you can subscribe to, like the ones listed in your excerpt from their documentation. This is great, but how do you integrate the two?
Well, I came up with the following solution (might not be the best, but hey, it works)
The idea is to stream the data from SQL record for record, but only flush the data to the caller in batches of 50. Once done, end the response.
I also needed it back in Array format so I had to construct the beginning, separators and end myself for this.
exports.listAllRecordsInReallyBigDataTable = (req, res) => {
const config = {
...
}
sql.connect(config, () => {
res.setHeader('Cache-Control', 'no-cache');
const request = new sql.Request();
request.stream = true;
request.query('select * from myBigTableOrView');
let rowCount = 0;
const BATCH_SIZE = 50;
request.on('recordset', () => {
res.setHeader('Content-Type', 'application/json');
res.write('[');
}
request.on('row', row => {
if (rowCount > 0)
res.write(',');
if (rows % BATCH_SIZE === 0)
res.flush();
res.write(JSON.stringify(row));
rowCount++;
}
request.on('done', ()=> {
res.write(']');
sql.close();
res.end();
};
};
};
right way to do this without any recordset consideration is
let request = new sql.Request();
request.stream = true;
request.query(*query*);
request.on('row', row => {
// Emitted for each row in a recordset
})
request.on('error', err => {
console.log(err);
// May be emitted multiple times
})
request.on('done', result => {
console.log("done ", result);
// Always emitted as the last one
})