Pulling information from API and adding it to database (MEAN stack) - javascript

I feel like a complete moron but me and a friend are working on a project together and having trouble getting the first route to return the items requested from the Etsy API and have getAllListings add the items to the database. If you can see something glaringly obvious that we are doing wrong please let me know.
I should also mention that while the statement console.dir(body) does print out the items to the terminal it does not look like the contents are being passed to GET '/api/etsy/getListings'
Thanks!
routes.js
//this i want to return a list of active listings from the users shop.
app.get('/api/etsy/getListings',function(req, res){
bEtsy.getAllListings(req, res, function(err, body) {
});
res.json(req.body);
});
bEtsy.js
var standardCallback = function (err, status, body, headers, callback) {
if (err) {
console.log(err);
return callback(err, null);
}
if (body) {
console.dir(body);
return callback(null, body); // this gives me an error
}
}
var getAllListings = function(itemId, callback){
var Item = mongoose.model('Item');
var listingsParams = {
include_private: true
}
etsy.auth().get(
'/shops/'+etsy.shop+'/listings/active',
listingsParams,
function(err, status, body, headers){
var newi = new Item({name: body.title, stock: body.count, owner: "00000000000000000000",
etsy:{listingId: body.listing_id, stock: body.count}});
newi.save(function(err){
if (err) return handError(err);
});
standardCallback(err, status, body, headers, callback);
}
);
};

You are calling this function with three parameters, when it only takes two
bEtsy.getAllListings(req, res, function(err, body) {
});
On top of that the first argument itemId is being passed the request object and then it is also never used inside the function itself but some global variable called listingsParams is?!?

Related

keyword search on title

The current code provides me with the data based on id of the API. My idea is to create a search based on keyword for it.
The idea is when you search for 'coding', it should log the data/posts with the keyword as a title.
function script(id) {
request(`https://hacker-news.firebaseio.com/v0/item/${id}.json`, function (error, res, body) {
if (error) {
console.log(error);
} else {
let myData = (JSON.parse(body))
var titleUrl = `https://news.ycombinator.com/item?id=${myData.id}`;
request(titleUrl, function (err, res, body) {
console.log(myData.title);
console.log(myData.score)
console.log(titleUrl)
});
}
});
}
script(23202120);
The second request does not return json instead it returns a html-page. You can use a html-parser like https://github.com/cheeriojs/cheerio to actually read the contents of the page.
...
request(titleUrl, function (err, res, body) {
const $ = cheerio.load(body);
const title = $('title');
console.log(title);
...
});
...

Trouble with callbacks, error catching and MongoDB

I've been working on an application which allows me to add companies to a database. Originally my code was pure spaghetti, so I wanted to modularize it properly. For this purpose, I added routes, a controller and a dao.
This is how my code looks right now
Routes
app.post('/loadcompanies', (req, res)=> {
companiesController.loadcompany(req.body, (results)=>{
console.log(results);
res.send(200, "working!");
})
})
Controller
module.exports.loadCompany = (body, callback)=>{
companiesDao.loadCompany(body, callback);
}
Dao
module.exports.loadCompany = (company, callback)=>{
MongoClient.connect(conexionString, (err, database) => {
if (err) console.log(err);
db = database;
console.log(company);
db.collection('companies').insert(company, (err, result)=>{
callback({message:"Succesfully loaded company", company:result});
});
})
}
My current concern is that working with errors when modularizing like this is confusing. I tried adding a try-catch method around the db insert and throwing and error if there is one, but that doesn't seem to work. Other things I've tried is returning the error in the callback, like this:
if (err) callback (err, null);
but I end up getting a "Can't set headers after they are sent." error.
How would you handle errors in this situation? For example, in the case that someone tries to add a duplicate entry in an unique element.
You should be able to simply do the error checking inside the callback for the insert function:
db.collection('companies').insert(company, (err, result)=>{
if (err) {
callback(err, null);
return;
}
callback(null, {message:"Succesfully loaded company", company:result});
});
If you get an error like you say, that's probably because the database is actually returning an error. You could also make your errors more specific, like:
module.exports.loadCompany = (company, callback)=>{
MongoClient.connect(conexionString, (err, database) => {
if (err) {
callback(new Error('Connection error: ' + err.Error());
return;
}
db = database;
console.log(company);
db.collection('companies').insert(company, (err, result)=>{
if (err) {
callback(new Error('Insertion error: ' + err.Error());
return;
}
callback(null, {message:"Succesfully loaded company", company:result});
});
})
Here is your loadCompany done in async / await format.
Notise there is no need for error checking, errors will propagate as expected up the promise chain.
Note I've also changed loadCompany to be an async function too, so to call it you can simply do var ret = await loadCompany(conpanyInfo)
module.exports.loadCompany = async (company)=>{
let db = await MongoClient.connect(conexionString);
console.log(company);
let result = await db.collection('companies').insert(company);
return {message:"Succesfully loaded company", company:result};
}

Making an api call inside a loop in node.js

I am trying to scan the top 100 movie torrents on the pirate bay using node and add a movie poster for each result.
I am using these libraries
thepiratebay
imdb-api
I am able to find top 100 and return the results with no problems
app.get('/movies', function(req, res){
tpb.topTorrents(207).then(function(topMovies){
async.map(topMovies, tpb.getTorrent, function(err, results){
res.send(results);
})
})
});
I am also able to look up movies via an IMDB ID and return the results
app.get('/imdb', function(req, res){
imdb.getReq({ id: 'tt2660888' }, function(err, things) {
res.send(things);
});
});
What I am trying to do is loop over the top 100 results pull the imdb id out of the description field out and query imdb replacing the picture field with result.
app.get('/movies', function(req, res){
tpb.topTorrents(207).then(function(topMovies){
async.map(topMovies, tpb.getTorrent, function(err, results){
for (var value of results) {
if (S(value.description).contains('www.imdb.com/title/')) {
var imdbId = S(value.description).between('www.imdb.com/title/', '/').s
imdb.getReq({ id: imdbId }, function(err, movie) {
value["picture"] = movie.poster
});
}
}
res.send(results);
})
})
});
This isn't working for some reason but it makes sense to me intuitively. If I remove the imdb-api call and replace it with value["picture"] = "foo". It does work. I'm not sure if this is related to how node handles loops. I'm new to the JS world and have a ruby background
Thanks in advance
You are on the right track with the async module but the imdb requests are also asynchronous so res.send just gets called with the initial result of async.map
You can use another async.map for the imdb calls and them chain them with async.waterfall which will pass the results of the first function as an argument to the second (async.apply just invokes the tpb function with your topMovies).
function tpb (topMovies, done) {
async.map(topMovies, tpb.getTorrent, done);
}
function imdb (movies, done) {
function lookup (value, callback) {
if (S(value.description).contains('www.imdb.com/title/')) {
var imdbId = S(value.description).between('www.imdb.com/title/', '/').s
imdb.getReq({ id: imdbId }, function(err, movie) {
value["picture"] = movie.poster
return cb(err, value);
});
} else {
return callback(null);
}
}
async.map(movies, lookup, done);
}
app.get('/movies', function(req, res){
tpb.topTorrents(207).then(function(topMovies){
async.waterfall([async.apply(tpb, topMovies), imdb], function (err, results) {
if (err) {
// do error handling
}
return res.send(results);
});
});
});

Weird Bug in my Javascript Scope

I Have run across a weird bug, where an exported function completely ignores the passed parameter and instead generates its own parameter.
My code is as follows:
exports.returnThumbs = function(req, res) {
var params = '{}, {thumb: 1}';
console.log(params);
PictureService.find(params, function(err, response){
if(err){
res.send(err);
} else {
console.log(response);
res.send(response, 'binary');
}
});
}
Which calls:
// Finds Answer by Passed Parameters
exports.find = function(params, callback){
console.log(params);
PictureModel.find(params, function(err, response){
if(err){
console.log(err);
callback({'error': 'Error retrieving answers'});
} else{
if(!response){
callback({'error': 'There are no pictures from this user_id'});
} else{
callback(null, response);
}
}
});
}
this should clearly pass '{}, {thumb: 1}' as the parameter, but the console.log instead prints: { user_id: 'thumbnails' } which is very weird. No matter what I do, renaming, refactoring. it always prints this as a parameter.
I ended up tracing it back to my router. I was parsing the request inappropriately.

Pattern for dealing with multiple I/O operations in Node.js

Suppose I have a CMS application written in Node.js which persists data on a Redis database. When this application creates a new content, it should increment the id counter, add the new id to a list of ides and then set a new hash with content. What I would do for now is to create a function to perform this execution. This function (let us call it createArticle()) would have a callback and would execute the increment. Once the increment was executed, a callback function would push it into the list of ids. After that, another callback would create the hash. The hash-creating callback would call the function passed as parameter to createArticle():
function createArticle(title, content, callback) {
var client = redis.createClient();
client.incr("idCounter", function(err, id) {
if (err) return callback(err, data);
client.lpush("articleIds", id, function (err, data) {
if (err) return callback(err, data);
var key = "article:"+id;
client.hmset(key, "title", title, "content", content, callback);
});
});
}
I would use this function more or less this way (using Express in this example):
app.post('/createarticle', function(req, res) {
var title = req.body.article.title,
content = req.body.article.content;
createArticle(title, content, function(err, data) {
if (err) return res.render('error', { status: 500, message: 'Internal Server Error' });
res.render('index', { status: 200, message: 'Article created!' });
});
});
However, this code looks a bit cumbersome to me. Is this the way to go. Or is there a better way to do a series of I/O steps? I used Express and Redis in my example, but the answer do not need to use them.
You can make those error-catchers single-lined:
function createArticle(title, content, callback) {
var client = redis.createClient()
client.incr("idCounter", function(err, id) {
if (err) return callback(err, data)
client.lpush("articleIds", id, function (err, data) {
if (err) return callback(err, data)
var key = "article:"+id
client.hmset(key, "title", title, "content", content", callback)
})
})
}
And you could use a helper for handling errors:
function noError(errorCb, cb) {
var slice = Array.prototype.slice
return function (err) {
var currentCb = err ? errorCb : cb
currentCb.apply(this, slice.apply(arguments, err?0:1)
}
}
function createArticle(title, content, cb) {
var client = redis.createClient()
client.incr("idCounter", noError(cb, function(id) {
client.lpush("articleIds", id, noError(function (data) {
var key = "article:"+id
client.hmset(key, "title", title, "content", content", callback)
}))
})
}
Or something like that.

Categories

Resources