18

I have a simple Lambda function that is supposed to take event data and send a message to an SNS topic.

Runtime is Node.js 8.10.

Here's my Lambda code in its entirety:

const AWS = require("aws-sdk");

exports.handler = async (event) => {

const sns = new AWS.SNS();

const emailBody = 
`
New Message

From: ${event.name}
Contact: ${event.contact}

Message: ${event.message}
`;

const snsMessageParams = {
    TopicArn: process.env.snsTopicArn,
    Message: emailBody
};

sns.publish(snsMessageParams, (err, data) => {
    if(err) {
        return err;
    } else {
        return data;
    }
  });
};

Every time I run the function I always get null in response. No error is returned, but I never get a message in my email from SNS, so there is something wrong here. I just can't figure out what.

I am able to successfully publish a message to my SNS topic from the SNS console, and from the Node SDK on my local computer. These work perfectly. I have checked that the snsTopicArn in my env variables is correct by copying and pasting it directly from my successful local code and the SNS console.

I have an Execution Role on my Lambda function that should allow me to publish to SNS on any topic in my account. I chose the default "SNSPublish" option when configuring the role for Lambda just to test everything. But for reference, here is the policy for my Execution Role:

{
  "roleName": <MyRoleName>,
  "policies": [
    {
       "document": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Action": [
                "sns:Publish"
              ],
              "Resource": "arn:aws:sns:*:*:*"
            }
          ]
        }
    ...
}

What is going wrong here? Any help is definitely appreciated.

John Rotenstein
  • 241,921
  • 22
  • 380
  • 470
Z_z_Z
  • 1,057
  • 4
  • 12
  • 22
  • Couple things: 1. not sure what *:*:* in the Resource section will do. I'd change it to "Resource": "*" to begin with, then put the sns arn in once you've got it working. 2. setup your lambda to write to cloudwatch (LambdaFullAccess or equivalent), then put some write lines in there, validating for example that the env variable is being read correctly, etc. I'd start with both of these steps. – Zaxxon Jul 21 '18 at 22:24
  • @Zaxxon Thanks for the advice, though I was able to solve it without having to do that – Z_z_Z Jul 21 '18 at 23:19

2 Answers2

29

Ok, so I figured it out.

There were two issues. The first is that I wasn't using the "callback" parameter that is used implicitly by Lambda for Node. The second was that the function was async by default, so it was expecting an "await" somewhere later. I decided just to remove the "async" and let it run synchronously since it is just a simple function and doesn't need it anyway.

Here is my updated, working code:

const AWS = require("aws-sdk");
const sns = new AWS.SNS();

exports.handler = (event, context, callback) => {

const emailBody = 
`
Message

From: ${event.name}
Contact: ${event.contact}

Message: ${event.message}
`;

const snsMessageParams = {
    TopicArn: process.env.snsTopicArn,
    Message: emailBody
};

sns.publish(snsMessageParams, (err, data) => {
    if(err) {
        return err;
    } else {
        callback(null, JSON.stringify(data));
    }
});
};
Z_z_Z
  • 1,057
  • 4
  • 12
  • 22
  • 1
    You can avoid the callback style and use async/await by using [promisify](https://nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_promisify_original). – Andy Gaskell Jul 22 '18 at 04:22
  • 2
    You're also not using calling the callback with the `err` i.e. it should be: ```if(err) { callback(err, { statusCode: 500, body: Error: ${err}` }); } else { callback(null, JSON.stringify(data)); }``` – Matthew O'Riordan Oct 10 '18 at 00:44
  • 2
    Docs for this section: https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html – Arian Acosta Jun 17 '19 at 18:40
9

In my case, I was just trying to execute basic helloWorld handler.

exports.helloWorld = (event, context) => {
    context.callbackWaitsForEmptyEventLoop = false;

    return {
        statusCode: 200,
        body: 'Hello World'
    }
}

I was trying with the above function but response was null. console.log was working fine. But after adding async to the function a proper response was shown.

exports.helloWorld = async (event, context) => {
Srinivas
  • 847
  • 11
  • 13
  • 1
    Thanks. It worked for me. Console.log was fine but returning 'null' as response. Now getting proper JSON formatted double-quoted key-value return. – Fakir May 30 '22 at 12:39