I am trying to create an async lambda function that makes a http Get call put it's not working correctly and I believe it has to do with it being async. If I remove await/async, and make it synchronous, my function works correctly. I'm not sure what I am doing wrong. Thank you for your help!
exports.handler = async function (event, context) {
await setBattery();
// TODO implement
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};
async function setBattery() {
'use strict';
const https = require('https');
const options = {
hostname: 'testsite.com',
port: 443,
path: '/proxy/api/setting?EM_OperatingMode=1',
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
};
const req = https.request(options, res => {
console.log(`statusCode: ${res.statusCode}`);
res.on('data', d => {
process.stdout.write(d);
});
});
req.on('error', error => {
console.error(error);
});
req.end();
}
According with MDN Web Docs:
The async function declaration defines an asynchronous function, which
returns an AsyncFunction object. An asynchronous function is a
function which operates asynchronously via the event loop, using an
implicit Promise to return its result. But the syntax and structure of
your code using async functions is much more like using standard
synchronous functions.
So, You need to return a Promise object in order to be handled by your async function (You could also use another promise to handle it), I converted setBattery to a normal function and the return of this function is now a promise that will be handled by your handler (exports.handler). I haven't tested the code but it should work.
exports.handler = async function (event, context) {
await setBattery();
// TODO implement
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};
function setBattery() {
'use strict';
const https = require('https');
const options = {
hostname: 'testsite.com',
port: 443,
path: '/proxy/api/setting?EM_OperatingMode=1',
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
};
// Return it as a Promise
return new Promise((resolve, reject) => {
const req = https.request(options, res => {
console.log(`statusCode: ${res.statusCode}`);
res.on('data', d => {
process.stdout.write(d);
// If successful
resolve(d);
});
});
req.on('error', error => {
console.error(error);
// If failed
reject(error);
});
req.end();
});
}
Related
I wrote the following Node.js code to create a table on HarperDB Cloud. This code executes perfectly when executed locally. I moved the same code to AWS Lambda, and alas, the code executed without any errors, but the table does not get created.
I suspect something wrong in the way I called lambda function, but I am unable to find out what's wrong. How can I fix it?
exports.handler = async (event,) => {
var https = require('follow-redirects').https;
var fs = require('fs');
var options = {
'method': 'POST',
'hostname': 'MyInstanceName-MyAccount.harperdbcloud.com',
'path': '/',
'headers': {
'Authorization': 'Basic MyAuthoriztionCode',
'Content-Type': 'application/json'
},
'maxRedirects': 20
};
var req = https.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function (chunk) {
var body = Buffer.concat(chunks);
console.log(body.toString());
});
res.on("error", function (error) {
console.error(error);
});
});
var postData = JSON.stringify({
"operation": "create_table",
"schema": "MySchema",
"table": "NewTable",
"hash_attribute": "Id"
});
req.write(postData);
req.end();
const response = {
statusCode: 200,
body: JSON.stringify('Table created successfully!'),
};
return response;
};
/// AWS Lambda Function (Node.js code) ends here'''
I believe this is because you are using a callback in an async lambda handler.
Try converting the lambda to a callback
exports.handler = (event, context, callback) => {}
or use an async/await request library.
Have a problem with nodejs, https.request returns <http.ClientRequest>
const checkStatus = await https
.request(
{
method: 'HEAD',
host: 'host',
path: 'path',
},
(response) => {
const { statusCode } = response;
// idk
},
)
.on('error', (e) => {
if (e) {
throw new Error();
}
})
.end();
Can i somehow return statusCode instead <http.ClientRequest> inside checkStatus variable?
You can only usefully await a promise but https.request does not return a promise.
Either:
Wrap it in a new Promise or
Replace it with a library which supports promises by default (such as Axios or node-fetch)
(async function () {
const url = "https://jsonplaceholder.typicode.com/todos/1";
const response = await axios.head(url);
console.log(response.status);
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js"></script>
I'm trying to develop a function in VS Code that takes an url as input and returns the response after processing is complete. However, when this function is run, it returns nothing. I tried testing similar code in Webstorm and and confirm that it console.logs the results just fine. I'm new to node and promises so not sure what I'm doing wrong.
Edit - added return keyword before driver.get as per the suggestion from #hellikiam. Also added a simple log statement to confirm that the results available yet not being returned in body.
var AxeBuilder = require('#axe-core/webdriverjs'),
WebDriver = require('selenium-webdriver');
const chromedriver = require('chromedriver');
const chrome = require("selenium-webdriver/chrome");
const screen = {
width: 640,
height: 480
};
chrome.setDefaultService(new chrome.ServiceBuilder(chromedriver.path).build());
var driver = new WebDriver.Builder()
.forBrowser('chrome')
.setChromeOptions(new chrome.Options().headless().windowSize(screen))
.build();
module.exports = async function (context, req) {
context.log('JavaScript HTTP trigger function processed a request.');
const url = (req.query.url || (req.body && req.body.url));
return driver.get(url).then(function () {
new AxeBuilder(driver).analyze(function (err, results) {
resultsJson = JSON.stringify(results);
console.log(resultsJson)
context.res = {
status: 200, /* Defaults to 200 */
body: resultsJson,
headers: {
'Content-Type': 'application/json'
}
};
if (err) {
// Handle error somehow
}
});
});
context.done();
}
you didn't returned anything from exported module.
return driver.get(url).then(function () {//add return statement in here.
new AxeBuilder(driver).analyze(function (err, results) {
resultsJson = JSON.stringify(results);
context.res = {
status: 200, /* Defaults to 200 */
body: resultsJson,
headers: {
'Content-Type': 'application/json'
}
};
if (err) {
// Handle error somehow
}
});
});
I found the other problem. What you want to return is inside callback function.
You should wrap the callback with promise to get the result outside of the callback. Try this out brother.
return new Promise((resolve, reject) =>{
driver.get(url).then(function () {//add return statement in here.
new AxeBuilder(driver).analyze(function (err, results) {
resultsJson = JSON.stringify(results);
context.res = {
status: 200, /* Defaults to 200 */
body: resultsJson,
headers: {
'Content-Type': 'application/json'
}
};
resolve(resultsJson)//Any value you want to return
if (err) reject(err)
});
});
})
Calling this function outside of module with await statement
const foo = require('./my/path/foo')
(async()=>{
const params = 'my param'
const bar = await foo(params)
console.log(bar)
})()
I'm working on load testing my API, but at some point I make a call to a different API.
Since I don't want to stress the second one, whenever I'm load testing I want to set a timeout and return an OK response like this:
function sendMessage(requestLib, blockApi, logger) {
if(!blockApi){
return (*my params*) => requestLib(`someURL`, {
headers: { Authorization: `Bearer ${token}` },
method: 'post',
data
});
}else{
logger.info("About to use the promise");
const response = returnOk.then(function() {
return new Response(200, {}, null, 'dummy.com');
});
return response;
}
}
returnOk is a Promise I defined earlier this way:
const returnOk = new Promise((resolve, reject) => {
setTimeout( function() {
resolve("Success!")
}, 2000)
});
And the function sendMessage is called inside a different function like this:
module.exports = ({ requestLib, Logger }) => async function(req, res) {
// Some unrelated code to decide if I'll call sendMessage
const response = await sendMessage(requestLib, blockApi, logger)(params);
// I log the response
res.end();
}
The regular flow works like a charm, but when I'm load testing and I get to returnOk.then()...
It throws
sendMessage(...) is not a function
If I remove the timeout and just return
return new Response(200, {}, null, 'dummy.com');
Things work just fine.
For some reason, your sendMessage(…) function returns another function:
return (*my params*) => …
You will need to do the same when mocking sendMessage:
function sendMessage(requestLib, blockApi, logger) {
if (!blockApi) {
return (*my params*) => requestLib(`someURL`, {
headers: { Authorization: `Bearer ${token}` },
method: 'post',
data
});
} else {
return (*my params*) => {
const returnOk = new Promise((resolve, reject) => {
setTimeout(resolve, 2000)
});
logger.info("About to use the promise");
return returnOk.then(function() {
return new Response(200, {}, null, 'dummy.com');
});
};
}
}
Btw, you really shouldn't have this boolean blockApi parameter to sendMessage. Write two distinct functions with the same signature, and use dependency injection in the code that is calling it.
I'm using the nodejs serverless module to create a lambda aws function.
'use strict';
const request = require('request');
const options = {
url: 'https://api.mysportsfeeds.com/v2.0/pull/nfl/2018-regular/games.json',
method: 'GET',
headers: {
"Authorization": "Basic " + Buffer.from("1da103"
+ ":" + "MYSPORTSFEEDS").toString('base64')
}
}
//this is automatically called by aws
module.exports.hello = async (event, context) => {
let result;
request.get(options, (error, response, body) => {
result = JSON.parse(body).lastUpdatedOn; //never happens cuz of async
});
return {
statusCode: 200,
body: JSON.stringify({
message: 'Go Serverless v1.0! Your function executed successfully!',
input: result,
}),
};
};
The problem I'm having is that I can't return output from the get request, because the assignment to the result variable (in the async get request) happens after the return statement. I don't think I can turn the outer function into a callback function for the get request. How could I work around this?
An alternative could be to extract the request logic and put it into a new function.
Remember, you need to catch any errors, so use a try-catch block for doing that.
'use strict';
const request = require('request');
const options = {
url: 'https://api.mysportsfeeds.com/v2.0/pull/nfl/2018-regular/games.json',
method: 'GET',
headers: {
"Authorization": "Basic " + Buffer.from("1da103"
+ ":" + "MYSPORTSFEEDS").toString('base64')
}
};
function getResult() {
return new Promise(function (resolve, reject) {
request.get(options, (error, response, body) => {
if (error) return reject(error);
resolve(JSON.parse(body).lastUpdatedOn); //never happens cuz of async
});
});
}
//this is automatically called by aws
module.exports.hello = async (event, context) => {
let result = await getResult();
return {
statusCode: 200,
body: JSON.stringify({
message: 'Go Serverless v1.0! Your function executed successfully!',
input: result,
}),
};
};