AWS lambda exits before callback hit - node.js

I am experiencing an issue with AWS Lambda with Node 8.10 existing early with the implicit null returned callback:
const request = require('request');
exports.handler = async (event, ctx, callback) => {
console.log(`Fetching '${event.url}'...`);
request(event.url, (err, body) => {
if (err) return callback(err);
return callback(null, body);
});
}
This is a basic example of the problem I have in my own much larger lambda function. Essentially, when I make a call to a 3rd party resource, the Lambda finishes before the callback is reached.
I have read several documentation pages and SO posts that refer to:
callbackWaitsForEmptyEventLoop but setting this to false does not change the behaviour.
I have managed to force it to wait for the response by wrapping the code in a promise then using await before calling callback but is this really necessary?
exports.handler = async (event, ctx, callback) => {
const executeLambda = new Promise((resolve, reject) => {
console.log(`Fetching '${event.url}'...`);
request(event.url, (err, body) => {
if (err) return reject(err);
return resolve(body);
});
});
await executeLambda
.then((res) => callback(null, res))
.catch(callback);
}
Note: I am mocking this locally with docker run --rm -v "$PWD":/var/task lambci/lambda:nodejs8.10 index.handler '{"url": "https://www.google.com"}'

The problem was that I declared the handler with async. The following works:
const request = require('request');
exports.handler = (event, ctx, callback) => {
console.log(`Fetching '${event.url}'...`);
request(event.url, (err, body) => {
if (err) return callback(err);
return callback(null, body);
});
}

Related

How can I make code wait for the return of an API call - Node.js

I am trying to write a function which makes an API call and wait until the API has returned a value, how do I improve my code to do this. At the moment it just returns Promise-pending.
async function getData(options){
const result = new Promise((resolve, reject) => {
request(options, function(error, response) {
if (error) return reject(error);
return resolve(JSON.parse(response.body));
});
});
var fromapi = await result;
return fromapi;
};
Try this
const request = require('request')
function makeRequest(uri, options) {
return new Promise((resolve, reject) => {
return request(uri, options, function (error, response) {
if (error) reject(error);
resolve(response.body);
});
});
}
async function getData(options) {
const result = await makeRequest('https://google.com', options)
console.log(result)
}
getData().then(() => {
}).catch((e) => {
console.log(e)
})

Listing cognito userpool users on AWS lambda

I'm trying to list all of my cognito users in my lambda function, however i get nothing in the return as if the callback not getting executed. What am I doing wrong?
The output of the code below just gives me a hello in the console.
var AWS = require("aws-sdk");
const cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();
export async function main() {
console.log("hello")
var params = {
UserPoolId: "myuserpoolid",
AttributesToGet: ["username"]
};
cognitoidentityserviceprovider.listUsers(params, (err, data) => {
if (err) {
console.log(err, err.stack);
return err;
} else {
console.log(data);
return data;
}
});
}
First of all, the structure of the code is wrong. The header of Lambda function should have a certain structure, either using async function or non-async function. Since you are using non-async code in your example I will show you how to do the later.
var AWS = require("aws-sdk");
const cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();
exports.handler = function(event, context, callback) {
console.log("hello")
var params = {
UserPoolId: "myuserpoolid",
AttributesToGet: ["username"]
};
cognitoidentityserviceprovider.listUsers(params, (err, data) => {
if (err) {
console.log(err, err.stack);
callback(err) // here is the error return
} else {
console.log(data);
callback(null, data) // here is the success return
}
});
}
In this case, Lambda will finish only when callback is called (or when it times out).
Similarly, you can use async function but you will need to restructure your code accordingly. Here is an example taken from official docs. Note how the promise wrapper is used.
const https = require('https')
let url = "https://docs.aws.amazon.com/lambda/latest/dg/welcome.html"
exports.handler = async function(event) {
const promise = new Promise(function(resolve, reject) {
https.get(url, (res) => {
resolve(res.statusCode)
}).on('error', (e) => {
reject(Error(e))
})
})
return promise
}
For AttributesToGet, don't use username because it is one of the fields that always gets returned. The following are members of the Attributes array, and can be used in the AttributesToGet field:
sub, email_verified, phone_number_verified, phone_number, email.
e.g.
AttributesToGet: ["email","email_verified"]

AWS Lambda doesn't work with `async`, Only with callbacks?

I have a Lambda function in AWS using Node 8.1 which initiate MSSQL request ( to an external server , which is not in aws)
When I'm using the non async handler with non async code (but callbacks )—
exports.handler = (event, context, callback) => {...}
— Everything is OK.
Proof: For this code, which uses callbacks-
exports.handler = (event, context, callback) => {
sql.connect(config, err => {
if (err) {
callback(err);
} else {
const req = new sql.Request();
req.query(
'select * from img.dbo.images where imageid = 1641',
(error, result) => {
if (error) {
callback(error);
} else {
sql.close();
callback(null, result.recordset);
}
}
);
}
});
};
I get this response :
However , If I change the code to the async version :
exports.handler = async (event, context, callback) => {
try {
let pool = await sql.connect(config);
let result1 = await pool
.request()
.query('select * from img.dbo.images where imageid = 1641');
callback(null, result1.recordset);
} catch (err) {
callback(err);
}
};
— I get a Timeout error(sure it's related to a non-fulfill promise) :
Question
Why doesn't the async version work ? how can I make it work?
Remove the callback param and just return the result instead. Also, you can avoid catching errors unless you need to do any special handling
exports.handler = async (event, context) => {
const pool = await sql.connect(config);
return await pool
.request()
.query('select * from img.dbo.images where imageid = 1641');
}

AWS Lambda: How to call Mysql Query in sequence

I want to call more than one sql queries in sequence.
I have tried with below code but getting timeout error:
exports.handler = function (event, context, callback) {
getNumber()
.then(result1 => {
// Use result1
return getNumber1(); // (A)
})
.then(result2 => { // (B)
console.log(result2);
callback(null, "OK");
})
.catch(error => {
console.log(error);
callback(null, "OK");
});
};
function getNumber() {
return new Promise(function(resolve, reject) {
connection.query("SELECT 1+1 as test ", (error, data) => {
if (error) {
reject(error);
} else {
resolve(data);
}
});
});
}
function getNumber1() {
return new Promise(function(resolve, reject) {
connection.query("SELECT 1+2 as test ", (error, data) => {
if (error) {
reject(error);
} else {
resolve(data);
}
});
});
}
Here I'm getting 'Task timed out after 3.00 seconds' error.
So anyone can help me to call connection.query synchronously.
This can be solved by chaining of AWS Lambda functions.
So in this case, each function has to be separated into different lambda functions. Then call them in a chain.
Still, keep in mind that Lambda functions have the time limit and will exit. So you may need different architecture/AWS component to achieve this if the Lambda running time is more.
Invoke multiple aws lambda functions

ctx.body undefined in async/await function

I'm trying to return from my node server with koa to my angular front end the result of an api call. Here's my controller which require a npm module which provides access to their api. Await should wait for the result and than return, am I wrong? I did something similar in a previous project but I was asking data from a db.
Why it is not working?
const color = require('colourlovers');
exports.getAllColors = async (ctx) => {
ctx.res.body = await color.get('/color/FFFFFF', { format: 'json' }, (err, data) => {
console.log(data);//<---here is logging the data
return data;
});
console.log(ctx.res.body);//<---here is undefined
ctx.status=200;
};
You can not await color.get because it uses callbacks instead of promises (well, you can await it, but it doesn't do what you'd expect). So to use await, you need to build the promise yourself:
ctx.res.body = await new Promise((resolve, reject) => {
color.get('/color/FFFFFF', { format: 'json' }, (err, data) => {
if(err) reject(err);
else resolve(data);
});
});
Now it'll wait for the promise to be resolved or rejected.

Resources