Express: Post Request handler doesn't stop calling a function - node.js

My express app should call a function once, but it repeatedly calls it an infinite number of times when handling a POST request. I can't figure out why it's called more than once.
This app works with Slack Events API and receives an event from Slack as a post request when a message is posted to a specific Slack channel. Once the app receives the event, it responds with a 200-status response to alert Slack it received it. Then the app extracts the text property from the request and calls postMessage with the text to post the message to a different channel. Posting the message to a different channel won't kick off another event.
The problem is that postMessage() gets called infinitely until I manually crash the app
Here I setup the app and wait for post requests:
const express = require('express');
var bodyParser = require('body-parser');
var app = express();
var jsonParser = bodyParser.json();
// Wait for post requests, then extract it's text
app.post('/', jsonParser, function (req, res) {
if (!req.body){
return res.sendStatus(400);
} else {
postMessage(req.body.event.text); //Should be called once
}
// Respond to Slack Event API we received their request
res.writeHead(200, {'Content-Type': 'application/json'});
res.end();
});
}
app.listen(config('PORT'), (err) => {
if (err) throw err
console.log(`App LIVES on PORT ${config('PORT')}`);
});
The body of the request is structured like:
body = {
event: {
text: "important message"
}
}
The function that keeps getting called. This will post a message to a Slack channel:
function postMessage(message){
var messagePath = 'https://slack.com/api/chat.postMessage?token=xxx&message=' + message;
request(messagePath, function(error, response, body){
if (!error && response.statusCode == 200){
console.log('message sent successfully');
} else {
console.log('error == ' + error);
}
});
}
The postMessage method does get called with the correct text. The problem is that it's called more than once.
I thought Slack's API was possibly sending the same request multiple times but from their documentation they will wait 3 seconds between sending requests. My app will call postMessage() about a hundred times in a second, so I don't think it's being overloaded with requests from Slack
Thank you in advance!

My guess is that your bot, which is listening to posted messages and then responding to those messages, is responding to itself when it posts. This will lead to an infinite loop.
The fix is to write a check to make sure the bot is not responding to itself. Inspect req.body.event and see if a username is being sent with each message. Then you can write something like this:
app.post('/', jsonParser, function (req, res) {
if (!req.body || !req.body.event){
return res.sendStatus(400);
} else if (req.body.event.user_name !== '<OUR_USER_NAME>') { // Make sure we are not responding to ourselves
postMessage(req.body.event.text); // Should be called once
}
// Respond to Slack Event API we received their request
res.writeHead(200, {'Content-Type': 'application/json'});
res.end();
});

Related

stopping postman after request is done

I am trying to create a Weather API using node. In my controller file, I have this code which is run for the /check route.
controller.js:
//Check Weather
exports.check = (req, res) => {
UserModel.check(req.body.city)
};
model.js:
//Check Weather
function getData(city) {
url = "something";
request(url, function (err, response, body) {
if(err){
console.log('error:', error);
} else {
console.log('body:', body);
}
});
}
exports.check = (city) => {
city = city.toLowerCase();
let values = getData(city);
console.log(city);
return(values);
};
route:
app.post('/check', [
UsersController.check
]);
When I run this, it functions properly and the correct thing is logged in the console. However, after I send a request in Postman and the console.log shows up, Postman seems to be hung up as seen in this pic. Is there anyway I can make it so that Postman stops sending the request after return or console.log?
Postman is waiting for a response from the server. Your code is not currently sending any response, so postman seems 'hung up' as it is waiting. Try changing the line saying UserModel.check(req.body.city) to say res.send(UserModel.check(req.body.city)) so it will send the data returned from your UserModel.check function back as the response. Alternatively, if you don't want to send back the returned value, you could just add res.send(PutWhateverYouWantSentHere) after the function call.

Send 'Received post' back to requester before async finishes (NodeJS, ExpressJS)

I have an API POST route where I receive data from a client and upload the data to another service. This upload is done inside of the post request (async) and takes awhile. The client wants to know their post req was received prior to the async (create project function) is finished. How can I send without ending the POST? (res.send stops, res.write doesn't send it out)
I thought about making an http request back to their server as soon as this POST route is hit. . .
app.post('/v0/projects', function postProjects(req, res, next) {
console.log('POST notice to me');
// *** HERE, I want to send client message
// This is the async function
createProject(req.body, function (projectResponse) {
projectResponse.on('data', function (data) {
parseString(data.toString('ascii'), function (err, result) {
res.message = result;
});
});
projectResponse.on('end', function () {
if (res.message.error) {
console.log('MY ERROR: ' + JSON.stringify(res.message.error));
next(new Error(res));
} else {
// *** HERE is where they finally receive a message
res.status(200).send(res.message);
}
});
projectResponse.on('error', function (err) {
res.status(500).send(err.message);
});
});
});
The internal system requires that this createProject function is called in the POST request (needs to exist and have something uploaded or else it doesn't exist) -- otherwise I'd call it later.
Thank you!
I think you can't send first response that post request received and send another when internal job i.e. createProject has finished no matter success or fail.
But possibly, you can try:
createProject(payload, callback); // i am async will let you know when done! & it will push payload.jobId in doneJobs
Possibility 1, If actual job response is not required:
app.post('/v0/projects', function (req, res, next) {
// call any async job(s) here
createProject(req.body);
res.send('Hey Client! I have received post request, stay tuned!');
next();
});
});
Possibility 2, If actual job response is required, try maintaining queue:
var q = []; // try option 3 if this is not making sense
var jobsDone = []; // this will be updated by `createProject` callback
app.post('/v0/projects', function (req, res, next) {
// call async job and push it to queue
let randomId = randomId(); // generates random but unique id depending on requests received
q.push({jobId: randomId });
req.body.jobId = randomId;
createProject(req.body);
res.send('Hey Client! I have received post request, stay tuned!');
next();
});
});
// hit this api after sometime to know whether job is done or not
app.get('/v0/status/:jobId', function (req, res, next) {
// check if job is done
// based on checks if done then remove from **q** or retry or whatever is needed
let result = jobsDone.indexOf(req.params.jobId) > -1 ? 'Done' : 'Still Processing';
res.send(result);
next();
});
});
Possibility 3, redis can be used instead of in-memory queue in possibility 2.
P.S. There are other options available as well to achieve the desired results but above mentioned are possible ones.

res.send(200) issue on facebook messenger bot

I am trying to build a facebook messenger bot using nodejs. I got the bot developed with core features. While testing for
a negative scenario where the user sends in a GIF or sticker, it has to respond "I couldn't get you. Sorry". It does send
that message but it hangs and keeps sending that message thereafter every few minutes. I noticed that the ngrok server threw an 500 HTTP
Internal Server Error for the POST request. On further debugging, I was able to find out that res.send(200) is not getting executed properly.
The console.log stmt that I have after res.send(200) never gets printed. Not sure what I may be missing. Any help is appreciated. Tried restarting the server and resubscribed to the app with a new ngork https link. The same message continues to get printed out :(.
Here is my code.
server.post('/', (req, res, next) => {
// Extract the body of the POST request
let data = req.body;
let incomingMessage = '';
if(data.object === 'page') {
data.entry.forEach(pageObj => {
// Iterate through the messaging Array
pageObj.messaging.forEach(msgEvent => {
incomingMessage = {
sender: msgEvent.sender.id,
timeOfMessage: msgEvent.timestamp,
message: msgEvent.message
}
});
});
}
const {
message,
sender
} = incomingMessage
if(message.text) {
f.txt(sender, 'Hi there!!!');
} else {
f.txt(sender, `I couldn't get you. Sorry :(`);
//res.send(200);
}
res.send(200);
console.log('We are at the end of post');
return next();
});
Maybe this answer doesn't resolve your problem but it can be helpful.
If you want to send a 200 HTTP code use this instead:
res.sendStatus(200); // equivalent to res.status(200).send('OK')
On the other hand, if this is not a middleware, you can remove the return next(); line.

How to return both immediate and delayed response to slack slash command?

I'm trying to use a hook.io microservice to make a slack slash command bot. According to the docs I should be able to send an immediate response then a seperate POST later. But I cant get the immediate response and the later POST to both work.
Here is my test code.
module['exports'] = function testbot(hook) {
var request = require('request');
// The parameters passed in via the slash command POST request.
var params = hook.params;
data = {
"response_type": "ephemeral",
"text": "Immediate Response"
}
hook.res.setHeader('Content-Type', 'application/json');
console.log("returning immediate response")
hook.res.write(JSON.stringify(data), 'utf8', delay(params));
//calling end() here sends the immediate response but the POST never happens.
// but if end() is called below instead slack gives a timeout error but the POST succeeds.
//hook.res.end()
//test with 3.5 second delay
function delay(params) {
setTimeout(function () {post_response(params)},3500);
}
function post_response(params) {
console.log("posting delayed response")
// Set up the options for the HTTP request.
var options = {
// Use the Webhook URL from the Slack Incoming Webhooks integration.
uri: params.response_url,
method: 'POST',
// Slack expects a JSON payload with a "text" property.
json: {"response_type":"in_channel", "text":"Delayed response","parse":"full"}
};
// Make the POST request to the Slack incoming webhook.
request(options, function (error, response, body) {
// Pass error back to client if request endpoint can't be reached.
if (error) {
console.log(error);
hook.res.end(error.message);
} else {
console.log("post OK");
}
// calling end() here sends the POST but the immediate response is lost to a slack timeout error.
hook.res.end()
})
};
}
As detailed in the comments calling res.end() early means the immediate response gets sent but the POST never happens whereas delaying the res.end() until after POST means the delayed response is sent but it generates a timeout error from slack in the meantime.
I'm a javascript newbie so hopefully there is a simple solution that I've overlooked.
Once you call res.end() inside hook.io, the script will immediately abort and end processing. It's the equivalent of calling process.exit. If you fail to end the request, hook.io will eventually hit it's own Time-out Limit.
hook.io should be capable of responding back to Slack within the three seconds Slack requires.
Here is a guide which may help: Making a Custom Slack Slash Command with hook.io
Bad form to answer one's own question I know but the following worked for me using webtask so I include it here in case others find it useful.
var express = require('express');
var Webtask = require('webtask-tools');
var bodyParser = require('body-parser');
var request = require('request');
var app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
//visiting above url in browser is handled by get
app.get('/', function (req, res) {
console.log(req.query)
res.send('OK');
});
//post from slack handled here
app.post('/', function (req, res) {
var params = req.body;
console.log(params);
var data = {
"response_type": "ephemeral",
"text": "Immediate Response"
}
res.setHeader('Content-Type', 'application/json');
res.send(data);
// deliberate delay longer than Slack timeout
// in order to test delayed response.
setTimeout(function () { post_response(params) }, 3500);
});
function post_response(params) {
console.log("posting delayed response")
// Set up the options for the HTTP request.
var options = {
// Use the Webhook URL supplied by the slack request.
uri: params.response_url,
method: 'POST',
// Slack expects a JSON payload with a "text" property.
json: { "response_type": "in_channel", "text": "Delayed response", "parse": "full" }
};
// Make the POST request to the Slack incoming webhook.
request(options, function (error, response, body) {
if (error) {
console.log(error);
} else {
console.log("post OK");
}
})
};
module.exports = Webtask.fromExpress(app);

How to "pass forward" a post() req to another api , get the res and send it back?

Background:
I am using building a system which uses 2 different 3rd parties to do something.
3rd party #1 - is facebook messenger app, which requires a webhook to connect and send info via POST() protocol.
3rd party #2 - is a platform which I used to build a bot (called GUPSHUP).
My server is in the middle between them - so, I need to hook the facebook messenger app to my endpoint on MY server (already did), so every message that the Facebook app get's, it sends to MY server.
Now, what I actually need, is that my server to act as "middleware" and simply send the "req" and "res" it gets to the other platform url (let's call it GUPSHUP-URL), get the res back and send it to the Facebook app.
I am not sure how to write such a middleware that acts like this.
My server post function is:
app.post('/webhook', function (req, res) {
/* send to the GUPSHUP-URL , the req,res which I got ,
and get the update(?) req and also res so I can pass them
back like this (I think)
req = GUPSHUP-URL.req
res = GUPSHUP-URL.res
*/
});
Yes , you can pass do request on another server using request module
var request = require('request');
app.post('/webhook', function (req, res) {
/* send to the GUPSHUP-URL , the req,res which I got ,
and get the update(?) req and also res so I can pass them
back like this (I think)
req = GUPSHUP-URL.req
res = GUPSHUP-URL.res
*/
request('GUPSHUP-URL', function (error, response, body) {
if(error){
console.log('error:', error); // Print the error if one occurred
return res.status(400).send(error)
}
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
console.log('body:', body); // Print the HTML for the Google homepage.
return res.status(200).send(body); //Return to client
});
});
2nd Version
var request = require('request');
//use callback function to pass uper post
function requestToGUPSHUP(url,callback){
request(url, function (error, response, body) {
return callback(error, response, body);
}
app.post('/webhook', function (req, res) {
/* send to the GUPSHUP-URL , the req,res which I got ,
and get the update(?) req and also res so I can pass them
back like this (I think)
req = GUPSHUP-URL.req
res = GUPSHUP-URL.res
*/
requestToGUPSHUP('GUPSHUP-URL',function (error, response, body) {
if(error){
return res.status(400).send(error)
}
//do whatever you want
return res.status(200).send(body); //Return to client
});
});
More info Request module

Resources