Sending Push Notifications with SMS in a Lambda Function

2023-03-07

#cloud#backend
Sending Push Notifications with SMS in a Lambda Function

This is a series of articles about setting up a complex Serverless backend infrastructure with AWS SAM and CloudFormation.

Here is the index of all the articles in case you want to jump to any of them:

1. Setup of AppSync and API Gateway with Multiple AWS Cognito User Pools

2. Configuring S3 Buckets with Permissions and Access Roles in AWS Cognito AuthRole

3. Intro to DynamoDB Resolvers for AppSync Implementation

4. Intro to Lambda Resolvers for AppSync Implementation

5. Configuring an AWS VPC to Include Lambda Resolvers with a Fixed IP

6. Intro to Pipeline Resolvers for AppSync Implementation

  1. Handling Lambda Resolver Timeouts with SNS Messages

A Lambda resolver times out. The user sees an error. But the operation you kicked off — maybe a long-running data migration, a report generation, an external API call — might still be running somewhere. You need a way to notify the system (or the user) when it completes.

SNS solves this. Fire an SMS when the operation finishes, fails, or hits a threshold. Lambda does the work; SNS delivers the message.

The Infrastructure

An SNS topic for SMS delivery, a Lambda function, and an IAM role that lets the function publish to the topic:

Resources:

  # SNS Topic for SMS
  SMSTopic:
    Type: "AWS::SNS::Topic"
    Properties:
      DisplayName: "SMSPushNotification"

  # Lambda Function to send SMS
  SendSMSPushFunction:
    Type: "AWS::Lambda::Function"
    Properties:
      Handler: "index.handler"
      Role: !GetAtt LambdaExecutionRole.Arn
      FunctionName: "SendSMSPush"
      Code:
        S3Bucket: "myBucket"
        S3Key: "code/sendSMSPush.zip"
      Runtime: "nodejs14.x"

  # IAM Role for Lambda Execution
  LambdaExecutionRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service: "lambda.amazonaws.com"
            Action: "sts:AssumeRole"
      Policies:
        - PolicyName: "LambdaExecutionPolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - "logs:CreateLogGroup"
                  - "logs:CreateLogStream"
                  - "logs:PutLogEvents"
                Resource: "arn:aws:logs:*:*:*"
              - Effect: "Allow"
                Action: "sns:Publish"
                Resource: !Ref SMSTopic

The Lambda Function

Takes a phone number and a message, publishes via SNS. That's the entire function.

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

exports.handler = async (event) => {
  const phoneNumber = event.phoneNumber;
  const message = event.message;

  const params = {
    Message: message,
    PhoneNumber: phoneNumber
  };

  try {
    const data = await sns.publish(params).promise();
    return { messageId: data.MessageId };
  } catch (error) {
    console.error(error);
    throw new Error('Failed to send SMS');
  }
};

Wire this into your pipeline resolver's error handling or as a final step after a long-running operation, and you have a notification system that works without polling, without WebSockets, and without any frontend infrastructure.

Code examples are simplified to illustrate the approach. Some adjustments may be needed for production.