When I'm trying to use "request" to access external API and get back the response in AWS Lambda, I don't know how to properly put my "return" code to return the response.
NodeJs 8.10
var request = require('request');
module.exports.sendcode = async (event) => {
let options = {
url: 'https://api.netease.im/sms/sendcode.action?' + content,
method: 'POST'
};
return await request(options, function (error, response, body) {
console.log(body);
return {
statusCode: 200,
body: JSON.stringify({
message: body,
input: event,
}),
};
});
};
When I run this code in serverless framework, I got a null response, there is nothing in the body, actually it should have at least the "input" attribute.
But console.log already logs the actual response from API.
It looks like my "return" code is not executed at all.
(If I remove async and await, then the program hangs until timeout)
Can anyone help how to modify this code to make it work?
Request does not use promises, so your await keyword is doing nothing. If you want to use promises with request, you need to find a library that supports promises, as noted here: https://www.npmjs.com/package/request#promises--asyncawait A popular option is https://github.com/request/request-promise-native
However, it is a simple matter to simply wrap a request in a promise, so that you don't need to use another dependency
var request = require('request');
const requestHandler = (options) => new Promise((resolve, reject) => {
request(options, (error, response, body) => {
if (error) {
console.error(error);
reject(error);
} else {
console.log(response, body);
resolve({ response, body });
}
});
});
module.exports.sendcode = async (event) => {
let options = {
url: 'https://api.netease.im/sms/sendcode.action?' + content,
method: 'POST'
};
const { response, body } = await requestHandler(options);
return {
statusCode: 200,
body: JSON.stringify({
message: body,
input: event,
})
}
};
Instead of writing your own promise wrapper, maybe request-promise pacakge with promise wrapper on top of request by the same authors, would be of interest to you.
const rp = require('request-promise')
module.exports.sendcode = async (event) => {
let options = {
url: 'https://api.netease.im/sms/sendcode.action?' + content,
method: 'POST'
};
const body = await rp(options)
console.log(body);
return {
statusCode: 200,
body: JSON.stringify({
message: body,
input: event,
}),
};
};
If you want full response you've to simply set resolveWithFullResponse to true
let options = {
url: 'https://api.netease.im/sms/sendcode.action?' + content,
method: 'POST',
resolveWithFullResponse: true
};
// now response contains full respose
const response = await rp(options)
// response.body
// response.statusCode
Use Async/Await with promises. You cannot use Async-Await syntax with callback. Either you must write a promise wrapper for your 'request'callback or you can use request-promise module which is already available.
I have already provided answer for similar problem which you can refer here.
Undefined value after returning an array of values from a MySQL query in a different file
Related
I am tracing my way through an application i am building. I have numbered steps to figure out what is getting called and what is not.
I call Axios and try to call .then() to get the data from the promise that is returned, but it is never executed. the application just keeps on going:
module.exports = async function request(requestOpts = {}) {
console.log("5: in request... sending request")
const response = await sendRequest({
method: requestOpts.method,
url: requestOpts.url,
headers: requestOpts.headers,
}).then(function(response) {
console.log("7: in request.. returning response")
return response;
})
};
function sendRequest(requestOpts = {}) {
console.log("6: in sendRequest.. returning axios")
return (
axios({
method: requestOpts.method,
url: requestOpts.url,
headers: requestOpts.headers,
}).then((response) => {
console.log("Then ===> " + response)
return response;
}).catch((error) => {
console.log("Error ==> " + error)
return error;
})
);
}
Step 7 never shows up.. things just move on and complete. Have I got this set up correctly? Here is the calling module:
module.exports = async function axiosCaller() {
console.log('4: in axiosCaller')
const authHeader = buildBasicAuth();
let response = await request({
url: 'redacted',
method: 'get',
headers: {
authHeader
},
}).then((response) => {
console.log(response);
return handleResponse(response);
});
}
I think you should not return the async response in .then instead you need to return axios response as promise as
async sendRequest() {
return await axios(....)
}
and while calling sendRequest function you need to mention await infront of that or simply use .then to capture the response
I'm new to AWS lambda functions and NodeJS. I'm trying to create an API Gateway call to a Lambda function that calls an external API and return some JSON data. It took me a while but I was finally able to get something to work based on this post:
AWS Lambda HTTP POST Request (Node.js)
The problem was the API Gateway kept erroring with a 502 Bad Gateway; which turns out to be that the JSON response was malformed. In the post I referenced above everyone seem to have success with just returning the JSON as-is, but I had to follow the instructions here to fix my issue:
https://aws.amazon.com/premiumsupport/knowledge-center/malformed-502-api-gateway/
My question is: if you look at the last 10 lines of my code that finally worked I had to reformat my response, as well as use a callback in a async function. I am new to nodeJS and Lambda but it looks wrong to me, even though it works. The post I referenced seem to have much more elegant code, and I hope someone can tell me what I am doing wrong.
const https = require('https');
var responseBody = {"Message": "If you see this then the API call did not work"};
const doGetRequest = () => {
return new Promise((resolve, reject) => {
const options = {
host: 'my.host.com',
path: '/api/v1/path?and=some¶meters=here',
method: 'GET',
headers: {
'Authorization': 'Bearer token for testing',
'X-Request-Id': '12345',
'Content-Type': 'application/json'
}
};
var body='';
//create the request object with the callback with the result
const req = https.request(options, (res) => {
res.on('data', function (chunk) {
body += chunk;
});
res.on('end', function () {
console.log("Result", body.toString());
responseBody = body;
});
resolve(JSON.stringify(res.statusCode));
});
// handle the possible errors
req.on('error', (e) => {
reject(e.message);
});
//finish the request
req.end();
});
};
exports.handler = async (event, context, callback) => {
await doGetRequest();
var response = {
"statusCode": 200,
"headers": {
"my_header": "my_value"
},
"body": JSON.stringify(responseBody),
"isBase64Encoded": false
};
callback(null, response);
};
I see couple of things.
We need to get the values from method doGetRequest and use the response, we can do that by await response = doGetRequest() or doGetRequest.then(), since we ant to capture errors as well, i went with second method.
We also need to resolve or reject the actual response from within promise.
I tested with a different api(with url of this question). Here is the updated code.
const https = require('https');
var responseBody = {"Message": "If you see this then the API call did not work"};
const doGetRequest = () => {
return new Promise((resolve, reject) => {
const options = {
host: 'stackoverflow.com',
path: '/questions/66376601/aws-api-gateway-with-lambda-http-get-request-node-js-502-bad-gateway',
method: 'GET'
};
var body='';
//create the request object with the callback with the result
const req = https.request(options, (res) => {
res.on('data', function (chunk) {
body += chunk;
});
res.on('end', function () {
console.log("Result", body.toString());
resolve(body);
});
});
// handle the possible errors
req.on('error', (e) => {
reject(e.message);
});
//finish the request
req.end();
});
};
exports.handler = (event, context, callback) => {
console.log('event',event, 'context',context);
doGetRequest().then(result => {
var response = {
"statusCode": 200,
"headers": {
"my_header": "my_value"
},
"body": JSON.stringify(result),
"isBase64Encoded": false
};
callback(null, response);
}).catch(error=> {
callback(error);
})
};
I created a login function that receives the mail and the pass, to receive the jwt. I have tried that the function returns the jwt but I have not succeeded.
This is the method that I have developed, it has a post request that sends the mail and pass parameters. in the resp variable I try to save the request response, but when invoking the function it prints :
undefined.
login(mail, pass) {
var options = {
'method': 'POST',
'url': 'https://inventario.demos.adlnetworks.com/api/login',
'headers': {
'Content-Type': 'application/json'
},
body: JSON.stringify({ "email": mail, "password": pass })
};
var resp;
var req = request(options, function(error, response) {
if (error) throw new Error(error);
resp = response.body;
});
return resp;
}
The problem is that "request" is an async function. You can't do this
var resp;
var req = request(options, function(error, response) {
if (error) throw new Error(error);
resp = response.body;
});
return resp;
Because "resp" always be undefined. You would need to do something like this
var resp;
var req = request(options, function(error, response) {
if (error) throw new Error(error);
return response.body;
});
But it wont work for you.
The short and easy solution is change the library to make http request, and use "async" and "await" to use easily async functions.
For example:
const fetch = require('node-fetch');
async function main(){
const data = await login();
console.log(data);
}
async function login(){
const url = "https://jsonplaceholder.typicode.com/posts";
const data = {
title: 'foo22222',
body: 'ba222r',
userId: 1
};
const response = await fetch(url, {
method: 'POST',
body: JSON.stringify(data),
headers: {
"Content-type": "application/json; charset=UTF-8"
}
});
const json = await response.json()
return json;
}
main();
In this case i use "node-fetch" library and consume a backend (in login function) that create a post and return its response.
I have a function in my project that processes most of my API requests (see simplified draft below)
function sendRequest(){
var reqbody;
var resp;
reqbody = unimportantRandomFunction();
request.post({
headers: { 'content-type' : 'application/xml',
'accept' : 'application/xml'},
url: process.env.APIURL,
body: reqbody
}, function(error, response, body){
resp = convert.xml2js(body, {compact:true});
});
return resp;
}
The problem I am facing is that request.post is asynchronous and by the time I get the response, an undifined value has already been returned.
I tried experimenting with promises (something I am entirely new to) but obviously I can't place my return in a .then block either because that return will not belong to the sendRequest function (correct me if I'm wrong).
Any ideas where I could put my return statement to make it work, that is wait for the value of "resp" to be assigned?
How about this modification? Please think of this as just one of several possible answers.
Promise is used sendRequest(). And the response value is retrieved in the async function. At the following modified script, at first sample() is run. And sendRequest() is run, then the response value is retrieved at console.log(res).
Modified script:
function sendRequest() {
return new Promise((resolve, reject) => {
var reqbody;
var resp;
reqbody = unimportantRandomFunction();
request.post(
{
headers: {
"content-type": "application/xml",
accept: "application/xml"
},
url: process.env.APIURL,
body: reqbody
},
function(error, response, body) {
if (error) reject(error);
resp = convert.xml2js(body, { compact: true });
resolve(resp);
}
);
});
}
async function sample() {
const res = await sendRequest().catch(err => console.log(err));
console.log(res);
}
sample(); // This function is run.
References:
Promise
async function
If I misunderstood your question and this was not the direction you want, I apologize.
i am new to node js coding i have two functions in my code one of them,i have made async using async keyword but the issue is it doesn't work the output from second function comes before the first function but i want the output of first function before the second function below given is my code
var request = require("request").defaults({jar: true});
var cookieJar = request.jar();
var options = { method: 'POST',
url: 'http://69.30.210.130:8082/api/session',
headers:
{ 'content-type': 'application/x-www-form-urlencoded' },
form: { email: 'admin', password: 'admin' } };
request(options, async function (error, response, body) {
if (error) throw new Error(error);
let bod=await body;
console.log(bod)
});
var options = { method: 'GET',
url: 'http://69.30.210.130:8082/api/devices',
qs: { id: '1' },
headers:
{ 'postman-token': '021a3566-e1ea-4dd4-4ceb-c81ecd25ddd1',
'cache-control': 'no-cache' } };
request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log(body);
});
request uses callbacks as default, it means when you want to chain multiple request, you have to do in this manner:
// first request
request({...}, function(error, response, body1) {
if (error) return console.error('Error', error.message);
// second request
request({...}, function(error, response, body2) {
if (error) return console.error('Error', error.message);
console.log(body1, body2);
});
});
async/await is meant to simplify working with promises, so you can use request-promise:
const rp = require('request-promise');
(async function() { // await can be called only from within an async func
try {
const body1 = await rp({...}); // first request
const body2 = await rp({...}); // second request
console.log(body1, body2);
} catch (e) {
console.error('Error', e.message);
}
})();
Here, the body2 will be resolved after body1 has been resolved. This means async/await brings a synchronous behavior into the asynchronous processing.
You can use axios which is Promises-base out of the box.
EDIT: async from callbacks removed, axios reference added