Home > Back-end >  Twilio API call works from local environment but not AWS Lambda function
Twilio API call works from local environment but not AWS Lambda function

Time:12-15

The Problem

I have the following code I want to run from a Lambda function (node.js v12 runtime):

const client = require('twilio')(process.env.ACCOUNT_SID, process.env.AUTH_TOKEN);

console.log("Start.");
client.messages.create({
  body: 'Msg',
  to: ' 1234567890',// a verified number
  from: ' 1234567890' // a valid Twilio number
})
.then((message) => {
  console.log(message.sid);
})
.catch((e) => {
  console.log(Error(e));
  console.log("Error in Signup");
});

console.log("Done.");

I can run this locally and I receive the message to the target number as I should, but when I zip it all up, move it to Lambda, and run it, I only get Start and Done, no output and no SMS from the create function.

Attempted Solutions

I've tried replacing the environment variables with the actual values just to make sure it's not an issue there, I've checked Twilio logs and there's nothing there aside from the successful calls I made locally.

I can confirm that every time it runs, it gets to client.messages.create and then it seems to just skip it, no output, Done is the next thing.

I got it to work one time, by adding a console.log(process.env.ACCOUNT_SID) statement before the first line (this was while using the environment variables and not the hard coded values), the output of the log statement was undefined but for whatever reason the message sent and it output the message's SID. Then it stopped working again.

Then a few hours later the same thing happened when I added a console.log(client.messages) after creating the client. And again it stopped working after that.

Any help would be hugely appreciated, I have never believed in poltergeists more than I do right now.

Full Code

Most of this is taken from here and here.

exports.handler = async (event, context, callback) => {
    const AWS = require('aws-sdk');
    const b64 = require('base64-js');
    const encryptionSdk = require('@aws-crypto/client-node');
    const Twilio = require('twilio');
            
    // Configure the encryption SDK client with the KMS key from the environment variables.  
            
    const { encrypt, decrypt } = encryptionSdk.buildClient(encryptionSdk.CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT);
    const generatorKeyId = process.env.KEY_ALIAS;
    console.log(process.env.KEY_ALIAS);
    const keyIds = [ process.env.KEY_ID ];
    console.log(process.env.KEY_ID);
    const keyring = new encryptionSdk.KmsKeyringNode({ generatorKeyId, keyIds });
    
    // Decrypt the secret code using encryption SDK.
          
    let plainTextCode;
    if(event.request.code){
        const { plaintext, messageHeader } = await decrypt(keyring, b64.toByteArray(event.request.code));
        plainTextCode = plaintext;
    }

    // Your Account SID from www.twilio.com/console
    // See http://twil.io/secure for important security information
    const accountSid = process.env.ACCOUNT_SID;
    console.log(process.env.ACCOUNT_SID);

    // Your Auth Token from www.twilio.com/console 
    // See http://twil.io/secure for important security information
    const authToken = process.env.AUTH_TOKEN;
    console.log(authToken);
            
    if(event.triggerSource == 'CustomSMSSender_SignUp'){
        console.log('CustomSMSSender_SignUp');
        // Send sms to end-user using custom or 3rd party provider.
        // Import Twilio's Node Helper library
        // Create an authenticated Twilio Client instance
        const client = require('twilio')(process.env.ACCOUNT_SID, process.env.AUTH_TOKEN);
        // Send a text message
        client.messages.create({
            body: `You're verification code is ${plainTextCode.toString()}.`,
            to: event.request.userAttributes.phone_number,  // user's phone number
            from: ' 1234567890' // redacted, actual value is a Twilio phone number
        })
            .then((message) => {
                // Success, return message SID
                console.log(message.sid);
            })
            .catch((e) => {
                // Error, return error object
                console.log(Error(e));
                console.log("Error in Signup");
            });
            
    }
};


While I'm here, bonus points if anyone can explain why I can print the KEY_ALIAS and KEY_ID env vars but ACCOUNT_SID and AUTH_TOKEN both get logged as undefined. They all do exist.

CodePudding user response:

Twilio developer evangelist here.

You're using a Lambda function marked async, which will return as soon as all processing is done within the function, including asynchronous processing as long as it uses the await keyword . The issue is that you make the call to the Twilio API, which is an asynchronous function, but do not use await, so the processing finishes straight after and the Lambda function is over.

AWS actually pauses the JS event loop and resumes it when the Lambda is called again, so you might even find messages being delivered seconds or minutes after you run the function, like this questioner did.

The way to solve it is to await the result of the call to the API. You can then wrap this in a try/catch to recover from errors, rather than using .then and .catch.

exports.handler = async (event, context, callback) => {
  const b64 = require("base64-js");
  const encryptionSdk = require("@aws-crypto/client-node");
  const Twilio = require("twilio");

  // Configure the encryption SDK client with the KMS key from the environment variables.

  const { decrypt } = encryptionSdk.buildClient(
    encryptionSdk.CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT
  );
  const generatorKeyId = process.env.KEY_ALIAS;
  console.log(process.env.KEY_ALIAS);
  const keyIds = [process.env.KEY_ID];
  console.log(process.env.KEY_ID);
  const keyring = new encryptionSdk.KmsKeyringNode({ generatorKeyId, keyIds });

  // Decrypt the secret code using encryption SDK.

  let plainTextCode;
  if (event.request.code) {
    const { plaintext } = await decrypt(
      keyring,
      b64.toByteArray(event.request.code)
    );
    plainTextCode = plaintext;
  }

  if (event.triggerSource == "CustomSMSSender_SignUp") {
    console.log("CustomSMSSender_SignUp");

    // Create an authenticated Twilio Client instance
    const client = Twilio(process.env.ACCOUNT_SID, process.env.AUTH_TOKEN);
    try {
      // Send a text message
      const message = await client.messages.create({
        body: `You're verification code is ${plainTextCode.toString()}.`,
        to: event.request.userAttributes.phone_number, // user's phone number
        from: " 1234567890", // redacted, actual value is a Twilio phone number
      });
      // Success, return message SID
      console.log(message.sid);
    } catch (error) {
      // Error, return error object
      console.log(Error(e));
      console.log("Error in Signup");
    }
  }
};

As for process.env.ACCOUNT_SID and process.env.AUTH_TOKEN being logged out as undefined, that suggests that they aren't set correctly. Make sure they are configured in the correct environment.

  • Related