Nodejs: returning result on async result - sql

I'm trying to code an RESTfull API in nodejs which is basically around a controller/modele schema and I meet some problems about the async nature of nodejs:
Station.js: (controller)
'use strict';
var url = require('url');
var Stations = require('./StationsService');
module.exports.stationsGet = function stationsGet(req, res, next){
var result = Stations.stationsGet(req.swagger.params['arg']);
if(typeof result !== 'undefined') {
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify(result || {}, null, 2));
}
else
res.end();
};
StationService.js: (modele)
'use strict';
exports.stationsGet = function(param){
var data_output = {};
var sql = 'SELECT * FROM foo WHERE args = ${foo}';
db.execute(sql, {foo: param}, db.queryResult.any, function(result){
// 'result' containing the query data
});
// Ideally: data_output = result;
return data_output;
}
The problem is if I use callback on my db.execute to continue, I have to give all the controller context (res, ...) to reply back to the client, and it break the modele/controller schema since my modele does the remaining controller work.
Is there a (easy?) way to get the result of the query in stationsGet() and then returning it?
Is it really against the nodejs nature and if so how to adopt the correct behavior in that case?
PS: I'm using swagger which has generated the files and base structure for nodejs.

You should use a callback in this case (take a look at promises as well)
your controller will look like this:
'use strict';
var url = require('url');
var Stations = require('./StationsService');
module.exports.stationsGet = function stationsGet(req, res, next){
Stations.stationsGet(req.swagger.params['arg'], function(err, result) {
if(typeof result !== 'undefined') {
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify(result || {}, null, 2));
}
else
res.end();
});
};
And the model you made must accept a callback as a last parameter, and you return err and the result like follows:
'use strict';
exports.stationsGet = function(param, cb){
var data_output = {};
var sql = 'SELECT * FROM foo WHERE args = ${foo}';
db.execute(sql, {foo: param}, db.queryResult.any, function(result){
cb(null, result); // first parameter is the error and the second is the result, this is pretty standard in node
});
}
I hope this helps you

Related

How to return blob item from localForage in service worker?

in my service worker i store mp4 video in indexedDB with localforage library in a blob data. It's work ! but i don't know how can i return this blob data.
This is my fetchHandler code :
const fetchHandler = async (event) => {
const getResponse = async () => {
const request = event.request;
if( request.destination === 'video' ){
// Get from indexedDB
const value = await localforage.getItem('video');
// How return the video from indexedDB cache ?
if( value ) return value; // not working
// Add in indexedDB
var networkResponse = await fetch(event.request);
localforage.setItem('video', networkResponse.blob() ).then(function (value) {
// Do other things once the value has been saved.
console.log(value);
}).catch(function(err) {
// This code runs if there were any errors
console.log(err);
});
}else{
const openedCache = await caches.open(SW_CACHE_NAME);
const cacheResponse = await openedCache.match(request);
if (cacheResponse) return cacheResponse;
var networkResponse = await fetch(event.request);
const cachePutResponse = await openedCache.put(request, networkResponse.clone());
if (cachePutResponse) return cachePutResponse;
}
return networkResponse;
};
event.respondWith(getResponse());
};
thanks for your help
You need to pass a valid Response object to event.respondWith(). That entails a response body (which is what you get back from localforeage.getItem()), but also some response headers.
You can use the Response constructor to create that, and return it from your getResponse() function.
The code could look something like:
const value = await localforage.getItem('video');
if (value) {
// See https://fetch.spec.whatwg.org/#bodyinit for what's accepted
// as a BodyInit.
return new Response(value, {
headers: {
// Replace this with the actual MIME type for the video.
'content-type': 'video/mp4',
// Include any other headers here if desired.
}
});
}

Use updated req variable in middleware after route execution

I have a middleware function I wrote to perform some logging for each HTTP request that comes in
export const winstonMiddlewareLogger = (req: express.Request, res: express.Response, next: express.NextFunction) => {
let _startTime = new Date();
res.on("finish", function() {
let responseTime = new Date().getTime() - _startTime.getTime() + "ms";
let method = req.method;
let url = req.originalUrl;
let body = req.body ? req.body : {};
let query = req.query;
let params = req.params;
let status = res.statusCode;
let msg = `HTTP ${req.method} ${req.url} ${responseTime}`;
let logDocument = {
msg,
method,
url,
params,
query,
body,
status,
responseTime,
};
logger.info(undefined, logDocument)
});
next();
};
Next thing I have a route
app.post("/slow", (req, res) => {
req.testParam = "test";
res.send("hello");
});
I want to be able to access the new property of the req parameter that is initiated in the /slow (testParam), in the res.on(...){...} event listener in my middleware function the next way:
if (req.hasOwnProperty('testParam')) {
console.log(req.testParam)
}
But the current state is that the req parameter is not updated no matter what I do in the route itself, because the req parameter it knows is only the one I get at the beginning of each request.
Changing the req variable in the route doesn't change it in the middleware which has already got a req variable of its own.
Any idea how to do it?
Ok so I found winston-express which actually does the things I wanted to.
What it does is to reassign the res.end function to a function he wrote himself.
This function can access the modified objects of req and res after the route has already executed.
So for the sake of showing how it works, I change my route to:
app.get("/", (req, res) => {
req._attributeFromRoute = "test";
res.send("hello");
});
And my middleware function looks something like this:
export const winstonMiddlewareLogger2 = (req: express.Request, res: express.Response, next: express.NextFunction) => {
let _startTime = new Date();
let end = res.end;
res.end = (chunk: any, encoding: string, cb?: () => void): void => {
res.end = end;
res.end(chunk, encoding, cb);
console.log(req._attributeFromRoute) // Notice that this property was not declared before the route execution, and now we can access it after it was defined
let responseTime = new Date().getTime() - _startTime.getTime() + "ms";
let method = req.method;
let url = req.originalUrl;
let body = req.body ? req.body : {};
let query = req.query;
let params = req.params;
let status = res.statusCode;
let msg = `HTTP ${req.method} ${req.url} ${responseTime}`;
let logDocument = {
msg,
method,
url,
params,
query,
body,
status,
responseTime,
};
logger.info(undefined, logDocument)
};
next();
};
And the way it will work is that the middleware function will wait for the route to execute and call the req.end function, which will enter the end function we created ourselfs.
We will then execute the original end function that had as a reference in the end variable.
Then we can do whatever we want with the updated objects of req and res.

Async/Await result is empty after await execution

I have a simple function:
var result = '';
var url = 'http://someurl.com/?action=perform-action';
(async function() {
let a = await fetch(url)
result = await a.text();
})()
console.log(result)
I'm trying to put the result into variable but it returns nothing.
My function simply checks if an email entered into an input exists in our database. Result is "Exists" or "OK". Works fine when I send it to the console but I need the result to perform other actions. If I save it to result it shows nothing.
Eventually your result variable will include the response from the fetch, but you're displaying the result before the fetch has completed its call.
Let's annotate the order:
var result = ''; // 1
var url = 'http://someurl.com/?action=perform-action'; // 2
(async function() {
let a = await fetch(url); // 5
result = await a.text(); // 6
})() // 3
console.log(result); // 4
If you moved your console.log call into the async function after the result is set you'd achieve your goal, like so:
var result = '';
var url = 'http://someurl.com/?action=perform-action';
(async function() {
let a = await fetch(url)
result = await a.text();
console.log(result);
})()
If the goal is to do something with that result outside the async call -- like somewhere else in your platform -- you'd need to observe the changes, or call an 'after result' function (which could be an injected callback.)
Here's a working example:
var result = '';
var url = 'https://jsonplaceholder.typicode.com/todos/1';
(async function() {
const a = await fetch(url);
result = await a.text();
console.log(result);
})()

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.js PostgreSQL requests are being terminated prematurely

I´m trying to perform updates on a PostgreSQL db using node but I´m getting an Error: Connection terminated performing UPDATE operation. I´ve probably messed up when handling the connections in a proper async way prematurely terminated them but can´t wrap my head on how I could perform that correctly.
Sorry for the big lump of code but it is pretty straightforward.
//main.js
var result = require('./processes.js');
result.check(function(message) {
console.log(message);
});
result.decrease(); //Throws an error.
//workers.js
var pg = require ('pg');
var fs = require('fs');
var db = JSON.parse(fs.readFileSync('../config.json', 'utf8')).db;
var querydb = require('./querydb.js');
var self = module.exports = {
check: function(callback) {
querydb.select("SELECT size FROM workers WHERE id = 1", function(results) {
callback(results[0].size);
});
},
increase: function() {
self.check(function(workers) {
querydb.update("UPDATE workers SET size = " + (workers + 1) + " WHERE id = 1");
});
},
decrease: function() {
self.check(function(workers) {
querydb.update("UPDATE workers SET size = " + (workers - 1) + " WHERE id = 1");
});
}
};
//querydb.js
var pg = require ('pg');
var fs = require('fs');
var db = JSON.parse(fs.readFileSync('../config.json', 'utf8')).db;
var select = function(statement, callback) {
pg.connect(db, function(err, client, done) {
var results = [];
//Handle errors
if(err) {
done();
console.log(err);
}
var query = client.query(statement);
//Stream results back
query.on('row', function(row) {
results.push(row);
});
//When all data is returned.
query.on('end', function() {
done();
callback(results);
client.end();
});
});
}
var update = function(statement) {
pg.connect(db, function(err, client, done) {
var results = [];
//Handle errors
if(err) {
done();
console.log(err);
}
var query = client.query(statement);
//When all data is returned.
query.on('end', function() {
done();
client.end();
});
});
}
module.exports = {
select: select,
update: update
};
Ok, this maybe isn´t an answer per se and more of a workaround but having synchronous calls for this kind of script (i.e. not a web server) was the desired behavior I wanted so I ended up going with pg-native instead of pg and refactored my querydb and workers modules to use it and now everything works!