Messaging and communication systems are an excellent fit for taking all the advantages of serverless cloud platforms and designs – highly scalable cost effective on demand pay per use infrastructure that can be defined as code without any of the hassles of a more traditional setup. In this blog I have two such full featured example applications – covering serverless emailing and also covering sms text messaging. Naturally, mobile push notifications also fit into this category very well and this post will cover how to do that by using Google’s firebase-admin python sdk with an AWS Lambda. This topic is not a full functionality application integrated with a dynamodb database or a front end user interface but a more concise serverless component that can be integrated as a microservice building block of a larger application – something I plan to cover in future articles.
Do note, this topic is not going to cover setting up a Firebase account to work with a sample app – we will focus on the simply building a Lambda to initialize an existing app using the python sdk and send push notifications.
Defining the Lambda yaml template
Check out this post for a more detailed explanation of working with AWS SAM, Cloudformation and Lambda. The first section below simply outlines the Lambda definition which we will code to send push notifications in the next sections. This Lambda needs to special IAM permissions or anything, though depending on your use case, you might want to consider defining cloudformation parameters to pass in values such as the firebase project id and such to the Lambda as environment variables. In this post those details are going to be hard coded within the Lambda code
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Sending Mobile Push Notifications with the firebase-admin sdk
Resources:
FirebasePushLambda:
Type: AWS::Serverless::Function
Properties:
Architectures:
- arm64
Runtime: python3.9
CodeUri: ./firebase-push-lambda/
Handler: firebase-push-lambda.event_handler
Description: Send Mobile Push Notifications with the firebase-admin sdk from inputs
FunctionName: sns-lambda
Creating the Python Lambda directory with the requirements.txt file
As outline above – we need to create a directory for our Lambda which can then be deployed as part of the cloudformation stack. This needs to have the same name as defined in your template yaml – in this case I have used firebase-push-lambda. The actual Lambda is a python file of the same name and the requirements.txt needs to have the dependency to indicate to Cloudformation that we need the firebase-admin sdk packaged along with our Lambda. We also need the pythong init file. The AWS SAM sdk will take care of the build, package and deploy magic as outlined in the post linked earlier.
-firebase-push-lambda
--__init__.py
--firebase-push-lambda.py
--requirements.txt
#inside requirements.txt - add
firebase-admin
Coding the push notification Lambda
Next up, we are going to code the Lambda. The Lambda will accept the basic inputs needed to populate the content of the push notificayion- title, data, body and of course the topic. The credentials certificate needed to initialize the fire-admin api is hard coded – replace with parameters specific to your project. Thinking ahead, depending on how you might consider using this base serverless block as a component for your own application, you might want to consider designs where even the project is dynamic – this would make sense when you have multiple apps you want to send push notifications to.
import firebase_admin
from firebase_admin import messaging
from firebase_admin import credentials
from datetime import timedelta
firebase_credential = {
"type": "#FILL",
"project_id": "#FILL",
"private_key_id": "#FILL",
"private_key": "#FILL",
"client_email": "#FILL",
"client_id": "#FILL",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "#FILL"
}
firebase_admin.initialize_app(credentials.Certificate(firebase_credential))
def event_handler(event, context):
topic = event["topic"]
data = event["data"]
title = event["title"]
body = event["body"]
message = messaging.Message(
android=messaging.AndroidConfig(
ttl=timedelta(seconds=3600),
priority='normal',
notification=messaging.AndroidNotification(),
),
apns=messaging.APNSConfig(
headers={
'apns-priority': '10'
},
payload=messaging.APNSPayload(
aps=messaging.Aps(
alert=messaging.ApsAlert(
title=title,
body=body,
),
mutable_content=True,
sound='default'
),
),
),
data=data,
topic=topic,
)
messaging.send(message, False)
The Lambda is pretty straightforward but lets break it down anyway.
We need to initialize the firebase admin sdk by passing it the credentials for your project. This can be a file or a python dictionary – which is what I have used. Since this isn’t dynamic it is a good practice to initialize this outside of the Lambda handler function – so AWS can reuse the Lambda for other requests without running through the initialization code each time. This should be how all initializations are done with Lambda – outside the handler.
The Lambda is accepting the events as part of its input payload – as seen where we extract the body, title, data and topic details of the request from the event. The signature changes slightly if you have this fronted by an API gateway, but you get the idea. Lastly, we make use of the messaging api in the sdk to define and send our push notification.
Concluding
This serverless microservice block can be integrated in a more complex design – repeating some of the considerations to that end – consider utilizing environment variables passed in as the template parameters for your stack. Consider designing the Lambda to be used with multiple apps and consider incorporating a Dynamodb data store to maintain a record of your notifications and statuses in case you need to resend.