REST Post API - retrieve result from TinyURL method - node.js

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.

Related

Who to Render Multiple fetch function in ejs

Multiple fetch res render function , hey I am trying to perform and multiple res render function in ejs but my data is lost is their any fix
app.get("/" , function(req,res){
fetch(`${domain}/popular`)
.then(res => res.text())
.then(datap => {
res.render('home',{datap : JSON.parse(datap)});})
fetch(`${domain}/recent-release`)
.then(res => res.text())
.then(datar => {
res.render('home',{datar : JSON.parse(datar)});})
})
i want to make multiple fetch function but the datar is replacing datap
Convert the function to async, await for the two requests, render only when they're done. (I also took the liberty of using the asynchronous res.json() parser instead of res.text() + JSON.parse.)
app.get("/", async function(req, res) {
const res1 = await fetch(`${domain}/popular`);
const datap = await res1.json();
const res2 = await fetch(`${domain}/recent-release`);
const datar = await res2.json();
res.render("home", { datap, datar });
});
(A future optimization could be to do the two requests in parallel using Promise.all().)

route is sending empty response

I have this controller
const getBalance = async (address, network) => {
const web3 = getWeb3Instance(network);
const currentBalance = await web3.eth.getBalance(address);
const formattedBalance = web3.utils.fromWei(currentBalance, 'ether');
return formattedBalance
};
This is how I use it in the route:
router.post('/getBalance', (req, res) => {
const {address,network} = req.body
try {
res.status(200).send(controllers.getBalance(address,network))
} catch (e) {
res.status(404).send(`${e}`)
}
});
When I console.log(formattedBalance) it logs the correct answer but in the response it is sending empty object {} and I don't know why. I'm using node.js with express and web3.
Any suggestions please?
You have the word async infront of your function. An function has an different behaviour if you put async infront of it. It acts like an promise.
Did you tried console.log(controllers.getBalance(address,network))? In the browser you would see Promise { <pending> }
The problem is you sending an pending promise back.
Change it to this. You need to wait till the promise resolves
router.post('/getBalance', async (req, res) => {
const {address,network} = req.body
try {
let balance = await controllers.getBalance(address,network);
res.status(200).send(balance)
} catch (e) {
res.status(404).send(`${e}`)
}
});

[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()
}

Nodejs read a file from an express web page

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/

Unit testing validation with express-validator

How can I unit test my validations that are done using express-validator?
I have tried creating a dummy request object, but I get the error: TypeError: Object #<Object> has no method 'checkBody'. I am able to manually test that the validation works in the application.
Here is what I have tried:
describe('couponModel', function () {
it('returns errors when necessary fields are empty', function(done){
var testBody = {
merchant : '',
startDate : '',
endDate : ''
};
var request = {
body : testBody
};
var errors = Model.validateCouponForm(request);
errors.should.not.be.empty;
done();
});
});
My understanding is that the checkBody method is added to the request object when I have app.use(expressValidator()) in my express application, but as I am only testing that the validation is working in this unit test I do not have an instance of the express application available, and the validation method that I am testing is not called directly from it anyway as it is only called through a post route, which I do not want to call for a unit test as it involves a database operation.
Here's a solution for the new express-validator api (v4):
tl;dr: You can use this function:
exports.testExpressValidatorMiddleware = async (req, res, middlewares) => {
await Promise.all(middlewares.map(async (middleware) => {
await middleware(req, res, () => undefined);
}));
};
It can be called like this:
const { validationResult } = require('express-validator/check');
await testExpressValidatorMiddleware(req, res, expressValidatorMiddlewareArray);
const result = validationResult(req);
expect(result....
These solutions assume you have the async/await syntax available. You can use the node-mocks-http library to create the req and res objects.
Explanation:
Each element in an express-validator array is applied to the route as middleware. Say this is your array:
[
check('addresses.*.street').exists(),
check('addresses.*.postalCode').isPostalCode(),
]
Each check will be loaded as middleware.
In order to test middleware, we need to implement a function which acts similarly to how express implements middleware.
Express middleware always accepts three params, the request and response objects, and the next function it should call (next by convention). Why do we need next? For scenarios where we want our middleware to do something before and after the proceeding function, e.g.
const loggerMiddleware = (req, res, next) => {
console.log('req body is ' + req.body);
next();
console.log('res status is ' + res.status);
};
But express-validator doesn't do this, it just calls next() once each of its validators is finished. For that reason, our implementation doesn't really need to bother with next().
Instead, we can just run each of our middlewares in turn and pass an empty function as next to avoid a TypeError:
middlewares.map((middleware) => {
middleware(req, res, () => undefined);
});
But this won't work, because express-validator middleware returns promises and we need to wait for them to resolve...
middlewares.map(async (middleware) => {
await middleware(req, res, () => undefined);
});
And we don't want to move on until all promises in our iteration are resolved (Mozilla docs on Promise.all are here):
await Promise.all(middlewares.map(async (middleware) => {
await middleware(req, res, () => undefined);
}));
And we should extract this as a reusable function:
exports.testExpressValidatorMiddleware = async (req, res, middlewares) => {
await Promise.all(middlewares.map(async (middleware) => {
await middleware(req, res, () => undefined);
}));
};
And now we've arrived at my solution. If someone can improve on this implementation, I'm very happy to make edits.
I faced the same issue and I had to create the methods using this:
var validRequest = {
// Default validations used
checkBody: function () { return this; },
checkQuery: function () { return this; },
notEmpty: function () { return this; },
// Custom validations used
isArray: function () { return this; },
gte: function () { return this; },
// Validation errors
validationErrors: function () { return false; }
};
function getValidInputRequest(request) {
Object.assign(request, validRequest);
return request;
}
So, in your code you have to call the getValidInputRequest helper:
describe('couponModel', function () {
it('returns errors when necessary fields are empty', function(done){
var testBody = {
merchant : '',
startDate : '',
endDate : ''
};
var request = {
body : testBody
};
request = getValidInputRequest(request); // <-- Update the request
var errors = Model.validateCouponForm(request);
errors.should.not.be.empty;
done();
});
});
Now, the request object has the body property and all the methods needed by express-validator.
If you want to test the cases that the validator fails, you should use something like this:
function getInvalidInputRequest(request, errorParams) {
// Get de default valid request
Object.assign(request, validRequest);
// Override the validationErrors function with desired errors
request.validationErrors = function () {
var errors = [];
errorParams.forEach(function(error){
errors.push({msg: 'the parameter "'+ error +'" is mandatory'})
});
return errors;
};
return request;
}
And to update the request you should do:
request = getInvalidInputRequest(request, ['mandatory_param_1', 'mandatory_param_2']);

Resources