Nodejs read a file from an express web page - node.js

i have a problem, I need to read a csv file from a web page developed using express.
Basically I have
router.get('/metricas', function(req, res, next) {
var datos= lee.leedatos();
res.render('metricas', {page:'Metricas', menuId:'metricas',data:datos});
});
and the lee.leedatos() is the following
exports.leedatos= function(){
var datos;
const csv =require('fast-csv')
const stream =fs.createReadStream('mywebsite/book1.csv');
const streamCsv=csv({
headers:true,
})
.on('data',data=>{
datos=data;
//console.log(datos);
})
.on('finish',()=>{
console.log('finished')
console.log(datos)
return datos;
})
stream.pipe(streamCsv);
}
My problem is that the web page always returns before the file has been read at all :( .. and data:datos is always empty.
How can I make the call syncronous?
Thanks !

You can use either callbacks / promises to track execution of asynchronous code. This is how code would look like with promises:
router.get('/metricas', function(req, res, next) {
var datos= lee.leedatos().then(() => {
res.render('metricas', {page:'Metricas', menuId:'metricas',data:datos});
});
});
exports.leedatos = function () {
return new Promise((resolve, reject) => {
var datos;
const csv = require('fast-csv')
const stream = fs.createReadStream('mywebsite/book1.csv');
const streamCsv = csv({
headers: true,
}).on('data', data => {
datos = data;
//console.log(datos);
}).on('finish', () => {
console.log('finished')
console.log(datos)
resolve(datos);
})
stream.pipe(streamCsv);
})
}
Here is a link to blog post that might help you in understanding asynchronous functions: https://blog.risingstack.com/node-hero-async-programming-in-node-js/

Related

REST Post API - retrieve result from TinyURL method

I failed to get a result from TinyURL within the POST method and assign it to "short_url" for the response. The console.log(short_url) will show "Promise { pending }". I tried async / await function to retrieve the TinyURL result but I'm sure I'm not using it right.
var express = require('express')
var TinyURL = require('tinyurl')
var app = express()
app.use(express.json())
app.use(express.static('public'))
app.get("/", function (req, res) {
res.sendFile(__dirname + '/index.html')
})
app.post('/api/shorturl', (req, res) => {
let original_url = req.body.url
console.log(original_url) // this one shows correct URL from request body
async function createShortURL(url) {
await TinyURL.shorten(url, function(res) {
console.log(res) // this one shows correct shortened URL
}
)}
let short_url = createShortURL(original_url)
console.log(short_url) // this one shows "Promise { <pending> }"
res.json({
original_url : original_url,
short_url : short_url
})
})
var listener = app.listen(process.env.PORT || 3000, function () {
console.log('Your app is listening on port ' + listener.address().port)
})
You're mixing async/await and callback. Don't do that. The tinyurl library provides the Promise version of shorten method. We can use async/await directly.
app.post('/api/shorturl', async (req, res) => {
let original_url = req.body.url
console.log(original_url) // this one shows correct URL from request body
// just this
let short_url = await TinyURL.shorten(url);
console.log(short_url)
res.json({
original_url : original_url,
short_url : short_url
})
})
EDIT
If you're using callback, please be aware of callback hell. It's one of the main reasons why people prefer async/await.

How to get and pass json from model to controller?

I'm trying to get json data from database (sql) with node.js and then pass it to app.get (express.js) but without success.
I have two files urls.js which should get all urls from database and in app.js I'm trying to create api endpoint with express.js. I have managed to get json data in app.js if I write query there and run it but I do not know how to separate it into two files.
code that works in app.js
app.get('/api/urls', (request, response) => {
db.query('SELECT * FROM urls', (error, result) => {
if (error) throw error;
response.send(result);
});
});
I've tried to separate it into two files so in urls.js (model like) I could have something like
class Urls {
async getUrls() {
const sql = `select * from urls`;
return await db.query(sql);
}
}
module.exports = Urls;
and then call it in app.js (controller like):
const data = new Urls();
app.get(/api/urls, (req, res) => {
res.send(data.getUrls());
}
In both cases result should be json
Your getUrls function is async it will return promise,so do something like this
const data = new Urls();
app.get(/api/urls, (req, res) => {
data.getUrls().then(response=>{
res.send(response);
})
}

[Koa]404 while passing throught the routeur

I'm having some trouble with the Koa framework. I'm trying to build a pretty basic server by I'm having a problem with my router. The ctx always return 404 despite passing in my functions.
Some code :
//www.js
const Koa = require('koa');
const app = new Koa();
const version = require('./routes/version');
app.listen(config.port, () => {
console.log('Server is listenning on port ' + config.port);
});
app.use(version.routes());
app.use(ctx => {
console.log ('test')
});
//version.js
const Router = require('koa-router');
const router = new Router();
router.prefix('/version');
router.use((ctx, next) => {
ctx.vFactory = new VersionFactory(ctx.app.db);
next();
});
router.get('/', getAllVersions);
async function getAllVersions(ctx, next) {
const ret = await ctx.vFactory.getAllVersions();
ctx.body = JSON.stringify(ret.recordset);
console.log(ctx.body)
await next();
}
I've checked a few threads. Most of the time, the problem seems to come from a non Promise based function in the await part of the router function. Here it is a simple DAO using mssql which is pretty promise based.
class DaoVersion {
constructor(db) {
this.pool = db;
}
async getAllVersions() {
const me = this;
return new Promise((resolve) => {
const ret= me.pool
.query(getVersion);
resolve(ret);
});
}
}
The console output seems good. I have my ctx.body set with my db data but if I try to check the whole context, I still have a 404. More interesting, if I try to ctx.res.write (using default node response) I got the "already end" message. So it seems Koa have sent the message before passing threw my function.
Any idea why and how I could correct that ?
Koa default response.status code is 404, unlike node's res.statusCode which defaults to 200.
Koa changes the default status code to 200 - when your route set's a non empty value to ctx.body or in some cases you can manually change (like if you need to set it to 202) it by using ctx.status = xxx.
You can use this documentation for reference: https://github.com/koajs/koa/blob/master/docs/api/response.md
Also, your route should be an async function:
router.get('/', async(ctx, next) => {
ctx.body = await getAllVersions
await next()
}

Bluebird promise resolve in express route

I have an simple REST application and I want to read files in my directory and send them back to frontend. There's code I'm using:
const fs = Promise.promisifyAll(require('fs'))
const router = require('express').Router()
router.get('/list', async (req, res, next) => {
const files = await fs.readdirAsync('presentations')
res.json(files)
})
The problem is: my frontend receive 'Promise', but if I try to debug it shows me that files is an array.
I've tried not to use async/await syntax like that:
router.get('/list', (req, res, next) => {
fs.readdirAsync('presentations')
.then(files => {
res.json(files)
})
})
But result was the same: frontend still get Promise.
UPD: Problem was with frontend axios instance. It didn't resolve promise, so await for results solved a problem.
So, there are three parts. Reading, storing and sending.
Here's the reading part:
var fs = require('fs');
function readFiles(dirname, onFileContent, onError) {
fs.readdir(dirname, function(err, filenames) {
if (err) {
onError(err);
return;
}
onFileContent(filename);
});
});
});
}
Here's the storing part:
var data = {};
readFiles('dirname/', function(filename) {
data[filename] = filname;
}, function(err) {
throw err;
});
The sending part is up to you. You may want to send them one by one or after reading completion.
If you want to send files after reading completion you should either use sync versions of fs functions or use promises. Async callbacks is not a good style.

Wait for data from external API before making POST request

I'm using the IBM Watson Tone Analyser API with Express.js and React. I have this code which sends some test to the Watson API:
// tone-analyser.js
class ToneAnalysis {
constructor() {
const params = {
username: process.env.USERNAME,
password: process.env.PASSWORD,
version_date: '2018-01-31'
}
this.Analyzer = new ToneAnalyzerV3(params);
}
ToneAnalyser(input) {
let tones = this.Analyzer.tone(input, (err, tone) => {
if (err) console.log(err.message)
let voiceTone = tone.document_tone.tones[0].tone_id;
console.log(voiceTone) // Logs the right value on Node.js console
return voiceTone;
});
return tones;
}
}
module.exports = ToneAnalysis;
I then use this on my Express backend like so:
// server.js
const ToneAnalysis = require('./api/tone-analyser');
const app = express();
const input = {
tone_input: 'I am happy',
content_type: 'text/plain'
}
app.get('/api/tone', (req, res) => {
let tone = new ToneAnalysis().ToneAnalyser(input);
return res.send({
tone: tone
});
});
And I make an API call from React here:
// App.js
componentDidMount() {
this.callApi()
.then(res => {
console.log(res.tone); // Logs the wrong value on Chrome console
})
.catch(err => console.log(err));
}
callApi = async () => {
const response = await fetch('/api/tone');
const body = await response.json();
if (response.status !== 200) throw new Error(body.message);
console.log(body);
return body;
};
I expect the value of res.tone to be a string showing the tone gotten from the tone analysis function (new ToneAnalysis().ToneAnalyser(input);). Instead, I get
{
uri: {...},method: "POST", headers: {...}}
headers: {...},
uri: {...},
__proto__: Object
}
I think this happens because the res.send(...) runs before tone has a value from the API. My question is, how do I make res.send(...) run only after tone has a value?
I tried wrapping the callback function in this.Analyzer.tone(input, [callback]) in an async/await block, but that did not fix the issue. Any ideas on how to fix this will be highly appreciated. Thanks!
If the call to
let tone = new ToneAnalysis().ToneAnalyser(input);
returns a promise then you could do something like
tone.then(res.send.bind(res))
If the call to
let tone = new ToneAnalysis()`enter code here`.ToneAnalyser(input);
returns a promise then you could do something like
tone.then(res.send.bind(res))

Resources