Integrating a callback from the server into a meteor/node method - javascript

would need a little help with upgrading my method for handling newsletter subscribers but don't really know how to do it. Basically I want to be able to catch the response from Mailchimp server when something is wrong (or right) to be able to process it.
Here is the code:
Meteor.methods({
subscribeToMailchimp:function(subscriberMail){
mailchimp.request({
method : 'POST',
path : Path,
body : {
"email_address": subscriberMail,
"status": "subscribed"
}
});
return true;
}
});
So according to docs of npm module: https://www.npmjs.com/package/mailchimp-api-v3 and his example:
mailchimp.request({
method : 'get|post|put|patch|delete',
path : 'path for the call, see mailchimp documentation for possible calls'
path_params : {
//path parameters, see mailchimp documentation for each call
}
body : {
//body parameters, see mailchimp documentation for each call
},
query : {
//query string parameters, see mailchimp documentation for each call
}
}, callback)
... i should be able to implement some callback in the end if I understand right. could anyone point me in the right direction to catch this response?
Thanks!

use err and results objects in callback
Meteor.methods({
subscribeToMailchimp: function(subscriberMail){
mailchimp.request({
method : 'POST',
path : Path,
body : {
"email_address": subscriberMail,
"status": "subscribed"
}
},function(err, results){ //here you can handle response
if(err){
console.log(err);
}else{
console.log(results);
}
});
}
});

To summarize other answers, the full snippet would look something like this (i can't test this particular request, but i think you get the point):
Meteor.methods({
subscribeToMailchimp: function(subscriberMail){
return Meteor.wrapAsync(function(callback) {
mailchimp.request({
method : 'POST',
path : Path,
body : {
"email_address": subscriberMail,
"status": "subscribed"
}
}, function(err, results) {
if (err) {
callback(err, null);
} else {
callback(null, results);
}
});
})();
}
});

If you want to send the actual response (error / results) of your remote service (Mailchimp in that case) to your client, you have to make your server Meteor method to "hang up", waiting for your asynchronous remote service request to complete, before you can let your method return.
Otherwise, the method will start the (asynchronous) request and continue its execution, i.e. return (as there is no more instructions in the method), therefore calling your client Meteor call's callback. Once the remote service request completes, the Meteor call is already finished, and only your server can perform some processing.
You could wrap your asynchronous request with Meteor.wrapAsync(), maybe adding a this.unblock() just before to let other Meteor methods process while waiting for the remote service to respond.
See also: Throwing Meteor.Error does not reach the client

Related

Handling a success in JS

I am experimenting with t library, and am trying to install the functionality into my own search bar to practice (still a student).
Can anyone provide advice to the format this would be in??
#PascalLamers answer is the cleanest way to go about it, using the "await" pattern.
To see what this looks like with promise chaining:
app.get('/:url', async function (req, res) {
return wappalyzer
.init()
.then(function(){
return wappalyzer.open(decodeURIComponent(req.params.url))
})
.then(function(site){
return site.analyze()
})
.then(function(data){
return res.status(200).json(data);
})
.catch(function(err){
return res.status(500).json({ message : err.message })
})
}
Each then executes a single async operation, and on success passes its result to the next then() operation.
Each then() is only called upon the success of the previous then(). If an error occurs at any point, the catch() is executed and the function returns (in this case, returns an error 500).
Completely ignoring what wappalyzer actually is or does, I would suggest the following, since you are already providing an async function as callback to your route controller :
app.get('/:url', async function (req, res) {
try {
await wappalyzer.init();
const site = await wappalyzer.open(decodeURIComponent(req.query.url));
const data = await site.analyze();
// if you end up here, everything was successfull
return res.status(200).json(data);
} catch (ex) {
// if you end up here, something went wrong
return res.status(500).json({ message : ex.message });
}
});
The above doesn't really make much sense, since you are telling your backend to look for an url param but using a query parameter instead. I woudln't recommend to send an url as either, param or query. Use the request body instead :
// receive request in backend, with an endpoint that makes a bit more sense
// also using POST method, otherwise you won't be able to send a body
app.post('/search', async function (req, res) {
const url = req.body.url;
// do stuff
});
// sending request to backend, using axios for example
const respond = await axios.post('/search', { url : 'www.google.com' });
// or using fetch
const respond = await fetch('/search', {
method: 'post',
body: JSON.stringify({ url : 'www.google.com' });
Please be aware these are just pointers, nothing to copy & paste :) .
If you are using Express, I suggest reading the documentation again. I think you are misunderstanding how the router works : https://expressjs.com/de/guide/routing.html#route-parameters

Need clarification on calling Meteor methods asynchronously

So i've been doing some reading and I think I have a general grasp on this subject but could use some insight from someone more experienced. I've been trying to write a simple RSS reader in Meteor and have been facing some issues with calling the Meteor method asynchronously. I currently define the method on the server(synchronously) and call it on the client(asynchronously). What I don't understand is that when I try to make the HTTP.call on the server, I return an undefined value passed to my client if I pass a callback into the request. But when I make the API request synchronously everything seems to work fine. Is this the normal behavior I should expect/the way I should be making the API call?
Meteor.methods({
getSubReddit(subreddit) {
this.unblock();
const url = 'http://www.reddit.com/r/' + subreddit + '/.rss';
const response = HTTP.get(url, {}, (err, res) => {
if(!err) {
//console.log(res.content);
return res;
} else {
return err;
}
});
}
});
Here's the method defined on the server side. Note that logging res.content shows that I'm actually getting the right content back from the call. I've tried reading some other answers on the topic and seen some things about using Future/wrapAsync, but I'm not sure I get it. Any help would be greatly appreciated!
The HTTP.get is doing async work, so callback passed to it will be called out of this meteor method call context.
To get desired result you should do it like this:
Meteor.methods({
getSubReddit(subreddit) {
// IMPORTANT: unblock methods call queue
this.unblock();
const url = 'http://www.reddit.com/r/' + subreddit + '/.rss';
const httpGetSync = Meteor.wrapAsync(HTTP.get);
try {
const response = httpGetSync(url, {});
//console.log(response.content);
return response.content;
} catch (err) {
// pass error to client
throw new Meteor.Error(...);
}
}
});

Node.js function without callback

I have a node.js server. When a user requests a page I call a function that pulls some info from db and services the request. Simple function with callback then execute response.send
I need to perform secondary computation/database updates which are not necessary for rendering the page request. I don't want the user to wait for these secondary ops to complete (even though they take only 200 ms.)
Is there a way to call a function and exit gracefully without callback?
You can simply do something like this
app.get('/path', function(req, res){
getInfoFromDatabase(); // get info from the database
res.render('myview', {info: data});
// perform post render operations
postRenderingCode();
return;
});
If I understand your problem correctly you can use setTimeout with a value of 0 to place the maintenance code at the end of the execution queue.
function service(user, callback) {
// This will be done later
setTimeout(function() {
console.log("Doing some maintenance work now...");
}, 0);
// Service the user
callback("Here's your data " + user);
}
service("John", function(data) { console.log(data); });
service("Jane", function(data) { console.log(data); });
The output will be:
Here's your data John
Here's your data Jane
Doing some maintenance work now...
Doing some maintenance work now...
You can call your extra ASYNCHRONOUS function before, or after your actual response; for example:
yourCoolFunction() // does awesome stuff...
response.writeHead(200, 'OK');
response.write('some cool data response');
response.end();
Note that the "yourCoolFunction" mentioned must be asynchronous, else the rest of the code will wait for it to complete.
Assuming you're using express.js:
function(req, res, next) {
doSomeAsyncWork(function(e, d) {
// Some logic.
doSomeMoreAsyncWork(function() {})
res.send(/* some data*/)
})
}
Basically you don't really care about the response of the additional async work so you can put in a function that does nothing for the callback.
since I can see none of the answers so far are even somehow helpful, and in order to avoid confusing. What I suggest is use on the object you are working on the following:
function doStuff() {
myObj.emit('myEvent', param);
}
function callback(param) {
do stuff;
}
myObj.on('myEvent', callback);
well, just do what you said, render the page, respond to the request and do whatever you have to do, your code isn't suddenly going to die because you responded to the request.
with express:
function handleTheRequest(req, res) {
res.status(200).send("the response")
// do whatever you like here
}

How to create a ajax POST with node JS?

I am not sure how to use an ajax POST to POST from a Jade Page to Node JS. If someone can provide an example or tell me what I am missing from the script I have, please let me know.
This is the script file:
//Add friends
$('.addContact').click(function() {
$.post('/addContact',
{friendRequest: $(this).data('user')});
if($(this).html!=='Contact Requested') {
return $(this).html('Contact Requested');
}
});
The url I have for a POST on my app.js file is:
app.post('/addContact', user.addContactPost);
I am trying to post true for a click event on the button Add Contact and change it to Contact Requested if the data in the db is shown as true.
This is the jade file:
extends layout
block content
div
legend Search Results
div#userResults
for user in ufirstName
a(href='/user/#{user.id}')
p #{user.firstName} #{user.lastName}
button.addContact Add Contact
The route file is this:
exports.addContactPost = function(req, res, err) {
User.findByIdAndUpdate(req.signedCookies.userid, {
$push: {friendRequest: req.body.friendRequest}
}, function(err) {
if(err) {
console.log("post2");
return console.log('error');
//return res.render('addContactError', {title: 'Weblio'});
}
else {
console.log('postsuccess');
//alert('Contact added');
res.json({response: true});
}
});
};
If you are posting AJAX request, then you are expecting from JS on client-side to get some response, and react to this response accordingly.
If it would be separate request to another page - then probably rendering whole page - would be actual option.
But as you just need to get response from server and then update your front-end without reloading based on response, then you need to response from server on this POST request with some JSON. And then on client-side, do some templating, use jQuery or some templating libraries on client side for it.
Everything looks good I just think the $.post code is a little off. This might fix your problem.
$('.addContact').click(function() {
$.post('/addContact', { addContact : true }, function(data){
console.log('posting...');
$('.addContact').html(data);
});
...
});
The object I added to the $.post is what is going to be sent to the server. The function you specified at the end is your callback. It's going to be called when the function returns. I think that may have been some of your confusion.
Your node route should look something like this
exports.addContactPost = function(req, res, err) {
User.findByIdAndUpdate(req.signedCookies.userid,{
addContact: req.body.addContact
}, function(err) {
if(err) {
console.log("post2");
res.render('addContactError', {title: 'Weblio'});
}
//assuming express return a json object to update your button
res.json({ response : true });
});
};

How in node.js send response with array of json objects?

I want to return array of finished pivotal stories using pivotal-node module for node.js.
app.get('/delivered_stories', function(request, response) {
pivotal.useToken("my_token");
pivotal.getProjects(function (err, data) {
var project_ids = data.project.map(function(x) { return parseInt(x.id); });
console.log('Retrived project ids: '.blue + project_ids);
project_ids.forEach(function(id) {
pivotal.getStories(id, { filter: "state:finished" }, function(err, story) {
response.send(story);
});
});
response.end(); // End the JSON array and response.
});
});
What I am doing wrong? And how to fix it? I get an error:
http.js:708
throw new Error('Can\'t set headers after they are sent.');
^
Error: Can't set headers after they are sent.
Whole code: https://gist.github.com/regedarek/30b2f35e92a7f98f4e20
pivotal.getStories() is asynchronous.
Its callback (and therefore response.send()) will run some time after the rest of your code (including response.end())
In fact, you shouldn't be calling response.end() at all; response.send() does that for you.
You also cannot call response.send() more than once; you need to combine all of the results into a single array and send that.
This is not simple to do; consider using async.js or promises.

Categories

Resources