Why is POST request showing undefined? Node JS - javascript

I am receiving a request response in the console.log, however when I try to post to Mastodon (similar site to Twitter), it posts as 'undefined'.
bot.js:
require('dotenv').config();
const Mastodon = require('mastodon-api');
var request = require('request');
console.log("Mastodon Bot starting...");
const M = new Mastodon({
access_token: process.env.ACCESS_TOKEN,
client_key: process.env.CLIENT_KEY,
client_secret: process.env.CLIENT_SECRET,
timeout_ms: 60*1000,
api_url: 'https://botsin.space/api/v1/',
})
const options = {
method: 'GET',
url: 'https://apisite.com/steps',
qs: {count: '3'},
headers: {
'X-RapidAPI-Key': 'secret-api-key',
'X-RapidAPI-Host': 'site-host',
useQueryString: true,
},
};
request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log(JSON.parse(body, undefined, 5));
});
const params = {
status: request
}
M.post('/statuses', params, (error, params) => {
if (error) {
console.log(error);
}
else {
console.log(params.request);
}
});
This posts "Mastodon bot is starting...", then the results of the API GET request to the console (the response body).
However, it does not post the response body to /statuses in Mastodon itself, it just posts 'undefined'.
How can I post the response body that I get in the console, to Mastodon?

Related

Why is POST request content 'undefined'?

When I make a POST request to a website, the JSON response body posts 'undefined'. However, it correctly prints the response body content in the form of name/value pair objects to the console when I call console.log(body).
Why is the body 'undefined' when I post the content to the website?
Response body:
const options = {
method: 'GET',
url: 'https://url.com',
content: {count: '3'},
headers: {}
};
Calls to body:
request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log(body);
});
const params = {
status: request.body
}
M.post('statuses', params) => {
console.log(params.request);
};

How to get a response from nodemailer using netlify functions?

I am using netlify functions to send an email from the frontend and it works fine... as in it does send the email.
However on the clientside (browser) I can't get any response. I need a basic response that would allow me to do a if (status==="success") displayMessage() but I can't seem to get any response on the browser.
I get this message Uncaught (in promise) SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data However on sending the request via POSTMAN I get a response 'Email Sent Succesfully' which is the body part of the callback response.
Here's the function that I am using at .netlify/functions/sendMail
const nodemailer = require("nodemailer");
exports.handler = function (event, context, callback) {
const mailConfig = {
host: "smtp.mailgun.org",
port: 465,
secure: true,
auth: {
user: process.env.MAILGUN_USER,
pass: process.env.MAILGUN_PASSWORD,
},
};
const transporter = nodemailer.createTransport(mailConfig);
transporter.verify((error, success) => {
if (error) {
console.log(error);
} else {
console.log("Ready to send emails");
}
});
const messageData = JSON.parse(event.body);
const { email, name, mobile, message, subject, recipient } = messageData;
console.log(messageData);
const mailOptions = {
from: email,
to: recipient,
subject: subject,
text: message,
};
transporter.sendMail(mailOptions, (error, success) => {
if (error) {
console.log(error);
callback(error);
} else {
console.log("email sent");
callback(null, {
statusCode: 200,
body: "Email sent successfully",
});
}
});
};
and on the client side I have this
const form = document.querySelector("#message");
const submitMessage = (event) => {
event.preventDefault();
const formData = new FormData(form);
formData.append("recipient", "testing#gmail.com");
formData.append("subject", "Submission from website");
const messageData = Object.fromEntries(formData);
console.log(messageData);
const url = ".netlify/functions/sendMail";
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(messageData),
};
fetch(url, options)
.then((response) => response.json())
.then((data) => console.log(data));
};
form.addEventListener("submit", submitMessage);
in the fetch request, I expected data to give me a response so I could trigger some action to display a success message..
Can someone advise me what I am doing wrong here?
I realised what I was doing wrong and here's the solution in case someone else faces the same issues.
In the callback from the netlify function I was sending the body as text
callback(null, {
statusCode: 200,
body: "Email sent successfully",
});
but in the clientside fetch request I was treating it as json
fetch(url, options)
.then((response) => response.json())
.then((data) => console.log(data));
So basically I could either use a response.text() instead of response.json() for the fetch request or use JSON.stringify and return a JSON object from the callback. I preferred the JSON.stringify option as below for the callback
callback(null, {
statusCode: 200,
body: JSON.stringify({
status: "success",
message: "Email sent successfully",
}),
});

request(...).then is not a function error when making a POST request

I'm trying to create a firebase function that makes a HTTP POST request whenever a new document is created.
This is my code:
import * as functions from 'firebase-functions';
const admin = require('firebase-admin');
const request = require("request");
exports.sendMessage = functions.firestore.document('comms/{comms}').onCreate((snap, context) => {
const newValue = snap.data();
if (newValue) {
//const email = newValue.email;
const msg = newValue.msg;
return request({
uri: "url",
method: 'POST',
body: msg,
json: true,
resolveWithFullResponse: true
}).then((response: { statusCode: number; }) => {
if (response.statusCode >= 400) {
throw new Error(`HTTP Error: ${response.statusCode}`);
}
console.log('SUCCESS! Posted', msg);
});
}
return Promise
});
Error received:
TypeError: request(...).then is not a function
at exports.sendMessage.functions.firestore.document.onCreate (/srv/lib/index.js:25:12)
at cloudFunction (/srv/node_modules/firebase-functions/lib/cloud-functions.js:127:23)
at /worker/worker.js:825:24
at
at process._tickDomainCallback (internal/process/next_tick.js:229:7)
request supports callback interfaces natively but does not return a promise, which is what you must do within a Cloud Function.
This is explained in the official Firebase video series here: https://firebase.google.com/docs/functions/video-series/. In particular watch the three videos titled "Learn JavaScript Promises" (Parts 2 & 3 especially focus on background triggered Cloud Functions, but it really worth watching Part 1 before).
You could use request-promise (https://github.com/request/request-promise) and the rp() method which "returns a regular Promises/A+ compliant promise". You would then adapt your code as follows:
import * as functions from 'firebase-functions';
const admin = require('firebase-admin');
const rp = require('request-promise');
exports.sendMessage = functions.firestore.document('comms/{comms}').onCreate((snap, context) => {
const newValue = snap.data();
if (newValue) {
const msg = newValue.msg;
var options = {
method: 'POST',
uri: '....',
body: msg,
json: true // Automatically stringifies the body to JSON
};
return rp(options)
.then(parsedBody => {
// POST succeeded...
console.log('SUCCESS! Posted', msg);
return null;
})
.catch(err => {
// POST failed...
console.log(err);
return null;
});
} else {
return null;
}
});
request module doesn't return a Promise instead try using a callback function for response.
return request({
uri: "url",
method: 'POST',
body: msg,
json: true,
resolveWithFullResponse: true
}, function (error, response, body) {
})
As in the documentation already mention you need to pass the callback to your request
var request = require('request');
request('http://www.google.com', function (error, response, body) {
console.log('error:', error); // Print the error if one occurred
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.
});
If you want to chain your request you can use pipe
request
.get('url/img.png')
.on('response', function(response) {
console.log(response.statusCode) // 200
console.log(response.headers['content-type']) // 'image/png'
})
.pipe(request.put('url'))
If you want to use promise you can use request-promise
var rp = require('request-promise');
rp('http://www.google.com')
.then(function (htmlString) {
// Process html...
})
.catch(function (err) {
// Crawling failed...
});
The request module work on callbacks only, If you want to make Promisify you need to do like this
const request = require('request');
const webService = {};
webService.callApi = (url, bodyObj, method) => {
return new Promise((resolve, reject) => {
const options = {
method: method || 'POST',
url: url,
headers: {
'Content-Type': 'application/json',
},
body: bodyObj,
json: true,
};
// Error Handler
const errorMessge = { code: 500, error: 'INTERNAL_SERVER_ERROR' };
request(options, (error, response, resBody) => {
if (error) {
return reject(errorMessge);
} else if (response.statusCode !== 200) {
return reject(errorMessge);
}
return resolve(resBody);
});
});
};
module.exports = webService;

Status-415 Invalid content-type specified error in nodejs

I would like to know how to send form data with headers to request module.
I have function getToken which makes post request with headers and formdata, and then response data will again make a request to new url and return data.
Currenlty returning
{ type: 'http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html',
title: 'Unsupported Media Type',
status: 415,
detail: 'Invalid content-type specified' }.
Apologies since not able to provide the apiurl and key
//app.js
var express = require('express');
var router = express.Router();
var helper= require('../help.js');
router.get('/', function (req, res) {
helper.getToken(req.originalUrl).then(token=>{
helper.getData(token).then(data=>{
res.send({api:data})
})
})
})
//helper.js
module.exports.getToken= function (rquery) {
return new Promise(async function (resolve, reject) {
try {
const form_data = {
grant_type: 'auth_token',
auth_token: ''//apikey,
}
var headers = {
"Content-Type": "multipart/form-data",
};
url = ``//apiurl1;
request.post({ url: url, form: form_data, headers: headers }, (e, r, body) => {
if (!e) {
resolve(JSON.parse(body));
}
else {
resolve(e);
}
});
}
catch (err) {
reject(err);
}
})
}
module.exports.getData= function (token) {
return new Promise(async function (resolve, reject) {
try {
const form_data = {
grant_type: 'refresh_token',
refresh_token: token.refresh_token,
}
var headers = {
"Content-Type": "multipart/form-data",
"Authorization":"Bearer "+token.access_token
};
url = ``//apiurl2;
request.post({ url: url, form: form_data, headers: headers }, (e, r, body) => {
if (!e) {
console.log(JSON.parse(body));// returns errors 415
resolve(JSON.parse(body));
}
else {
resolve(e);
}
});
}
catch (err) {
reject(err);
}
})
}
It seems that the API you are posting to does not support multipart/form-data. Can you confirm the Content-Type on the API?
EDIT: Found this in npmjs page of request module: https://www.npmjs.com/package/request#multipartform-data-multipart-form-uploads
The key that you are sending is "form". Try sending "formData" instead

Using the Node Request module with AWS Lambda and API Gateway

This lambda function works:
exports.handler = function(event, context, callback) {
const done = (err, res, func) => callback(null, {
statusCode: '200',
body: res,
headers: { 'Content-Type': 'application/json' },
})
done(null, 'works'))
}
I can hit the API Gateway end point and receive the response message, 'works'.
However, when I add a request call (POSTing a message to Slack) that looks like this:
const request = require('request')
const moment = require('moment-timezone')
exports.handler = function(event, context, callback) {
// the json object is built following these docs:
// https://api.slack.com/docs/message-attachments
const SLACK_WEB_HOOK_URL = process.env.SLACK_WEB_HOOK_URL
const uri = SLACK_WEB_HOOK_URL
const method = 'POST'
const options = {
json,
uri,
method,
}
const slack = (opts) => {
request.post(opts, (err, response, body) => {
if (response.statusCode < 300) {
return 'Success!'
} else {
return 'Err!'
}
})
}
const done = (err, res, func) => callback(null, {
statusCode: '200',
body: res,
headers: { 'Content-Type': 'application/json' },
})
done(null, slack(options))
}
The server hangs when I hit the API end point ...
And I get a Task timed out after 10.00 seconds error:
{
"errorMessage": "2017-06-23T16:41:21.483Z c385e32e-5832-11e7-8c4f-673b524cf783 Task timed out after 10.00 seconds"
}
But my Slack POST request gets sent and I see the message in the channel.
How do I send the POST request and then wait for it to return, and then exit the lambda function with a custom response message?
Thanks for the help!
Put the callback into the callback of the post method
request.post(opts, (err, response, body) => {
if (response.statusCode < 300) {
return callback(null, {
statusCode: '200',
body: res,
headers: { 'Content-Type': 'application/json' },
});
} else {
return callback(err);
}
});

Categories