I'm currently working on writing a function on AWS Lambda. I want to convert a RSS feed into a JSON and give that as a response in the body when making to the Lambda endpoint.
I'm using an npm package to convert the RSS to JSON. However, when I run the code. I see that I get undefined in the conversion of the RSS URL. Here is the following code:
const feed = require('rss-to-json');
exports.handler = async (event) => {
let rssFeed = event.queryStringParameters.rssFeed;
let rssAsJsonData = convertRssIntoJson(rssFeed);
return sendRes(200, rssAsJsonData);
};
const sendRes = (status, body) => {
var response = {
isBase64Encoded: true|false,
statusCode: status,
headers: {
"Content-Type": "application/json"
},
body: body,
};
return response;
};
function convertRssIntoJson (rssFeed) {
console.log(rssFeed);
return feed.load(rssFeed, function(err, rss){
if(err) {
console.log("Error: ${err}");
return;
}
console.log(rss)
return rss;
});
};
However, in the logs I get undefined
when console.log(rssAsJsonData)
.
However, when debugging I was able to see console.log(rss)
working when I change body to be body: json.stringify("TESTING")
However, it only worked when logging to the console not when I tried to pass it to the body body: body,
I can't seem to find what the error is. I'm moving from Ruby to JavaScript for this project maybe I'm missing something.
function convertRssIntoJson (rssFeed) {
console.log(rssFeed);
return feed.load(rssFeed, function(err, rss){
if(err) {
console.log("Error: ${err}");
return;
}
console.log(rss)
return rss;
});
};
The piece of code above is a callback. Under the hood, feed.load is asynchronous, which makes your callback be executed asynchronously.
Now, when you invoke your function like this
let rssAsJsonData = convertRssIntoJson(rssFeed);
your rss
object inside convertRssIntoJson
does not hold any value yet, because the callback hasn't been populated up to now. This is where your undefined
comes from.
Callbacks themselves don't make code asynchronous by default, but NodeJS works with a non-blocking IO model and, since feed.load is an IO call, it will be executed asynchronously.
You have a few options now, but I will list only two. A not-so-nice and a nice solution:
1) The not-so-nice way to fix it is to add a callback as argument to your convertRssIntoJson
function and pass the value of that rss
object upstream. The not-so-nice full code can be found below:
const feed = require('rss-to-json');
exports.handler = async (event) => {
let rssFeed = event.queryStringParameters.rssFeed;
convertRssIntoJson(rssFeed, (err, data) => {
if (err) {
return sendRes(500, { message: 'There was an err: ' + err.message })
}
return sendRes(200, data)
})
};
const sendRes = (status, body) => {
var response = {
isBase64Encoded: true | false,
statusCode: status,
headers: {
"Content-Type": "application/json"
},
body: body,
};
return response;
};
const convertRssIntoJson = (rssFeed, callback) => {
console.log(rssFeed);
feed.load(rssFeed, function (err, rss) {
if (err) {
console.log("Error: ${err}");
callback(err, undefined)
}
console.log(rss)
callback(undefined, rss)
});
};
2) The nice, clean, elegant and recommended solution is this one. Wrap your callback in a Promise, like this
function convertRssIntoJson(rssFeed) {
console.log(rssFeed);
return new Promise((res, rej) => {
feed.load(rssFeed, function (err, rss) {
if (err) {
console.log("Error: ${err}");
return rej(err)
}
console.log(rss)
return res(rss)
});
})
};
Since your handler is async
, it means it can just await
on Promises.
So your client code is now as simple as:
return sendRes(200, await convertRssIntoJson(rssFeed));
Your final code will look like (I have refactored a little bit to make use of arrow functions):
const feed = require('rss-to-json');
exports.handler = async (event) => {
let rssFeed = event.queryStringParameters.rssFeed;
return sendRes(200, await convertRssIntoJson(rssFeed));
};
const sendRes = (status, body) => {
var response = {
isBase64Encoded: true | false,
statusCode: status,
headers: {
"Content-Type": "application/json"
},
body: body,
};
return response;
};
const convertRssIntoJson = (rssFeed) => {
console.log(rssFeed);
return new Promise((res, rej) => {
feed.load(rssFeed, (err, rss) => {
if (err) {
console.log("Error: ${err}");
return rej(err)
}
console.log(rss)
return res(rss)
});
})
};
If you want to know more about async/await, you can see it in here.
EDIT: Code refactor and code added for solution 1)