Main Problem: Lambda function is not exiting after successful external API call, subsequently triggering multiple external API calls.
I have a lambda function that is listening for slack events when an event takes place the lambda function is invoked. I filter these events using some condition checks, and when the targetted event takes place I make an external API call.
The above all works, however, the external API call is being triggered multiple times per one event (~4 times when it should be 1 if successful).
Idempotent - Often people associate this issue with idempotency, however, given its the external API call within a condition block that is being triggered additional times I do not believe this an issue.
statusCode 200
before the subsequent (additional) external API callscontext.done()
and redundant callback()
with no luckLooking for some suggested approaches
I have attached the lambda function below:
const axios = require('axios');
const sendMessages = async(event, context, callback) => {
// test the message for a match
if (event.type === 'message' && event.bot_id !== undefined) {
console.log("filter events for specific event");
let URL = 'https://someurl.com/slack/events';
let response = await axios.post(URL, {
events: event,
});
console.log("response success :: ", response.status);
if (response.status === 200) {
console.log("condition met");
context.done();
callback(null, 'successful request');
}
}
callback(null, 'successful request');
};
// Lambda handler
exports.server = (data, context, callback) => {
let eventData = JSON.parse(data.body);
switch (data.path) {
case "/slack-events": sendMessages(eventData.event, context, callback); break;
default: callback(null);
}
};
Expected behavior
The problem (you are not the first nor will you be the last one asking this type of question, AWS needs to fix the docs ASAP) is because you are mixing up async/await
with context.done
and callback()
calls.
If your functions already are async
, then stick with await
all the way through and forget about context
and callback
objects.
See that sendMessages
is async
, therefore it returns a Promise, but you don't do anything with that Promise. You should await
on it. I have changed your code accordingly and got rid of both context
and callback
objects, you don't need them.
const axios = require('axios');
const sendMessages = async (event) => {
// test the message for a match
if (event.type === 'message' && event.bot_id !== undefined) {
console.log('filter events for specific event');
let URL = 'https://someurl.com/slack/events';
let response = await axios.post(URL, {
events: event,
});
console.log('response success :: ', response.status);
return response;
}
return Promise.resolve({});
};
// Lambda handler
exports.server = async (event) => {
let eventData = JSON.parse(event.body);
switch (event.path) {
case '/slack-events':
await sendMessages(eventData.event);
break;
}
return {
message: 'Success',
}
};
If this Lambda is invoked by API Gateway, then you need to return a 2xx status code (or 4xx and 5xx in case of errors) with a stringified body so it can terminate properly, like so:
return {
statusCode: 200,
body: JSON.stringify({message: 'Success'})
}