How to use an asynchronous response value in an `app.get` callback - javascript

I want to use flickrapi (https://www.npmjs.com/package/flickrapi) package. I need to authorize it:
Flickr.tokenOnly(flickrOptions, function(error, flickr) {
//I need this flickr variable
});
and I want to use this flickr variable in my express code
app.get('/', function (req, res) {
//do something with flickr
});
How should I do it?

Modular approach:
Put your flickr connectivity code separate:
flickr-public.js
var Flickr = require("flickrapi"),
flickrOptions = {
api_key: "API key that you get from Flickr",
secret: "API key secret that you get from Flickr"
};
module.exports = (function(){
Flickr.tokenOnly(flickrOptions, function(error, flickr) {
//handle error here
console.log('Flickr Object Obtained');
return flickr;
});
})();
Note: Better instantiate the flickr object in your app.js file.
So that the object gets created immediately when server starts. As this flickr object is for public API only and does not need authentication again and again.
You can instantiate the flickr object by simply requiring it in app.js file:
require('./flickr-public');
Now Simply access flickr object anywhere by simply requiring it.
routes.js
const flickr = require('../path-to/flickr-public');
app.get('/', function (req, res) {
//use flickr object to perform actions.
});
Explanation:
From the node.js documentation:
Modules are cached after the first time they are loaded. This means (among other things) that every call to require('foo') will get exactly the same object returned, if it would resolve to the same file.
Multiple calls to require('foo') may not cause the module code to be executed multiple times.

Just put it inside your get
app.get('/', function (req, res) {
Flickr.tokenOnly(flickrOptions, function(error, flickr) {
//do something res.status(200).send('what you want here');
});
});

use it directly inside your route callback
app.get('/', function (req, res) {
Flickr.tokenOnly(flickrOptions, function(error, flickr) {
//call someother method to get photos etc. and finally call res.send()
res.send(photos); // where photos is obtained from flickr or anything you can pass which should be response of you request.
});
});

Related

how to send additional data along with res.redirect in NodeJS Express?

As we can send additional data or "message" along with res.render like in the example below, can we do the same in res.redirect?
res.render("dashboard", {message:"Welcome to the dashboard page"});
you can use session for passing data with redirect
flash package - https://www.npmjs.com/package/connect-flash
app.get('/flash', function(req, res){
// Set a flash message by passing the key, followed by the value, to req.flash().
req.flash('info', 'Flash is back!')
res.redirect('/');
});
app.get('/', function(req, res){
// Get an array of flash messages by passing the key to req.flash()
res.render('index', { messages: req.flash('info') });
});
there are a couple of ways in which you can pass data around in a redirection but the most right way is to use query string to pass your data. as said you always need to make sure that your URL is properly encoded
app.get('/redirect', function(req, res) {
var string = encodeURIComponent('something that would break');
res.redirect('/your/redirection/url/?data=' + string);
});

How to route through a path and perform different functionality at different URLs?

I would like to post at the path /users and then immediately post to /users/:id, but the actions need to be different at each of these URLs, so I can't use the array method for applying the same middleware to different URLs
The idea is that POST(/users/:id, ...) will never be called by the client. It only gets called immediately after POST(/users, ...)
When using express, you are providing a handler function for a specific endpoint. Actually it's an array of those functions (middlewares). That means that you can switch from :
route.post('/users/`, (req, res, next) => {
// do your magic
});
to
route.post('/users/', handleMyCall);
This way you can easily reuse those functions in multiple endpoints without your need to actually make requests:
route.post('/users/', (req, res) => {
// do something +
handleMyCall(req, res);
// either return the result of this call, or another result
});
route.post('/users/:userID', (req, res) => {
// do another operation +
handleMyCall(req, res);
});
Update:
Using GET or POST differs in the way the data is sent to the server. You can use both for your cases, and it really depends on the testing client you have.
Typically, a GET request is done to query the database and not do any actions. POST is usually used to create new entities in the database.
In your scenario, I'd guess you would have post('/users/) in order to create a user. And then have get('/users/:userID') to find that user and return it to the client.
You can easily have different endpoints with different handles for those cases.
As I understood from the comments, you'll need a POST request on /users (to persist data in some database) and GET /users/:id to retrieve these data, which is very different from POSTing the same thing on 2 different endpoints.
POST is generally used to persist and GET to retrieve data.
I'll assume you use some kind of NoSQL DB, perhaps MongoDB. MongoDB generate a unique ID for each document you persist in it.
So you'll have to have 2 routes :
const postUser = async (req, res, next) => {
try {
// persist your user here, perhaps with mongoose or native mongo driver
} catch (e) {
return next(e);
}
}
const getUserById = async (req, res, next) => {
try {
// get your user here thanks to the id, in req.params.id
} catch (e) {
return next(e);
}
}
export default (router) => {
router.route('/users').post(postUser);
router.route('/users/:id').get(getUserById);
};

Send data with Javascript function in Express response

I want to send an application/javascript response from my Express server, passing the data which I have got from MongoDB.
This is a response to a call for loading some in a third party website.
I have created all the different parts of the process, now just need to pass on the data into the Javascript response.
server.js
app.get('/', (req, res) => {
productInfo(param1, param2, res)
}
productInfo.js - MongoDB call
function productInfo(param1, param2, res){
Product.find({key1: param1}, (err, docs) => {
let idList = docs.idList;
res.set('Content-Type', 'application/javascript');
res.sendFile(__dirname + '/script.js', (err) => {
if (err) { console.log(err) }
else { console.log('file sent') }
});
}
module.exports = productInfo;
script.js - sending a self executing anonymous function
(function(){
// function - load jQuery & Bootstrap in 3rd party website
$masterDiv = $(`
<div>
...
... *data required*
</div>
`)
$('body').append($masterDiv);
// function - jquery event handlers where *data is required*
})();
When some event happens on the third party website page, the event handlers update the right data (id).
How do I pass along data (idList) to script.js?
If I set dummy global variables data before the (function(){})(); line in script.js then I can access it within the function.
I tried res.render but it says "Cannot find module 'js'".
res.render(__dirname + '/scriptproduct.js', (err) => {});
Can I somehow set params to script.js function and call the function with res.send(functionName(idList))?
I have seen answers with templates being sent in html views with res.render but how do I use such a solution in my case where the data is required both in JS and HTML?
I have lots of other routes which are not using a template engine. Can I use it for just one route if that is the solution?
I am very new to all this and basically hacking forward to a solution. So some of my questions above might be elementary.
Using ejs you can pass a string to a EJS template or a .js file. However, you can only pass strings. What you could do is pass the object as a string using JSON.stringify(obj) and then use JSON.parse(obj) to convert it back.
Here's another answer that has some code and may help: How to include external .js file to ejs Node template page

What javascript library sets the _parsedUrl property on a request object

I am working with node/express/passport/ looking at code that attempts to use a request like:
req._parsedUrl.pathname;
I cannot figure out where this variable is coming from. Is this a canonical variable name that is set in a common .js library? It doesn't seem exposed in any headers.
req._parsedUrl is created by the parseurl library which is used by Express' Router when handling an incoming request.
The Router doesn't actually intend to create req._parsedUrl. Instead parseurl creates the variable as a form of optimization through caching.
If you want to use req._parsedUrl.pathname do the following instead in order to ensure that your server doesn't crash if req._parsedUrl is missing:
var parseUrl = require('parseurl');
function yourMiddleware(req, res, next) {
var pathname = parseUrl(req).pathname;
// Do your thing with pathname
}
parseurl will return req._parsedUrl if it already exists or if not it does the parsing for the first time. Now you get the pathname in a save way while still not parsing the url more than once.
You can write a middleware to handle then set properties for req.
var myMiddleWare = function () {
return function (req, res, next) {
req._parsedUrl = 'SOME_THING';
next()
}
};
app.get('/', myMiddleWare, function (req, res) {
console.log(req._parsedUrl); // SOME_THING
res.end();
})
Express middleware document in here

Express | Making multiple http requests asynchronously

I am currently building a small node application that makes a few api calls and renders a webpage with charts on it. I'm using express and jade as the render engine.
The problem is that I'm quite new to javascript and I don't know how to scheme out my http requests so I can pass an object of variables I got from the api (http get) when there is more than one request. I don't know how to map it out to make a single object and send it to the jade rendering engine.
Here is what I have so far :
app.get('/test', function(req, res) {
apiRequestGoesHere(name, function(error, profile) {
//Get some data here
});
anotherApiRequest(tvshow, function(error, list) {
//Get some data here
});
res.render('test', data);
});
As it is right now, the page renders and the requests are not done yet, and if I place res.render inside one of the request, I can't access the other's data.
So what I want is a way to set it up so I can have multiple api calls, then make an object out of some elements of what is returned to me from the rest api and send it to Jade so I can use the data on the page.
You probably want to use async to help with this. async.parallel is a good choice for something simple like this:
app.get('/test', function(req, res) {
async.parallel([
function(next) {
apiRequestGoesHere(name, function(error, profile) {
//Get some data here
next(null, firstData);
});
},
function(next) {
anotherApiRequest(tvshow, function(error, list) {
//Get some data here
next(null, secondData);
});
}], function(err, results) {
// results is [firstData, secondData]
res.render('test', ...);
});
});
The first argument to those functions next should be an error if relevant (I put null) - as soon as one is called with an error, the final function will be called with that same error and the rest of the callbacks will be ignored.
You can async parallel.
async.parallel([
function(callback){
// Make http requests
// Invoke callback(err, result) after http request success or failure
},
function(callback){
// Make http requests
// Invoke callback(err, result) after http request success or failure
}
],
// optional callback
function(err, results){
// the results array will be array of result from the callback
});
The reason your page renders is the callbacks haven't "called back" yet. To do what you want, you would need to do something like:
app.get('/test', function(req, res) {
apiRequestGoesHere(name, function(error, profile) {
//Get some data here
anotherApiRequest(tvshow, function(error, list) {
//Get some data here
res.render('test', data);
});
});
});
This strategy leads to what is known as "pyramid code" because your nested callback functions end up deeper and deeper.
I would also recommend the step library by Tim Caswell. It would make your code look something like:
var step = require('step');
app.get('/test', function(req, res) {
step(
function () {
apiRequestGoesHere(name, this)
},
function (error, profile) {
if error throw error;
anotherApiRequest(tvshow, this)
},
function done(error, list) {
if error throw error;
res.render('test', list)
}
)
});
You could also use the group method to make the calls in parallel and still maintain the sequence of your callbacks.
Gl,
Aaron

Categories