I decided to take on more AWS-based projects to gain a deeper understanding of the services. This specific project was a serverless messaging application. It utilizes a state machine at the center of the application to go through a simple process to send either an email message, text message, or both.
In this project, I used:
- State Machine via Step Functions
- Lambda Function interacting with Simple Email Service (SES)
- Lambda Function interacting with API Gateway
- Simple Notification Service (SNS) to send text messages
- S3 bucket for static hosting of Frontend
Configuring the SES and Lambda Function
Simple Email Service is a good name for this service because it's a very easy setup. It's very straight forward to set up the emails and verify them.
The Lambda function is definitely trickier. The function is coded in Python 3.8 and pulls in SES from the boto3 client. This has a method called send_email() that will queue an email up for sending. You must use a verified email from SES to send the message. Additionally, only SES verified email addresses can receive the message.
This is also where I created the IAM role for the Lambda function. This will allow for CloudWatch to create a log stream...and this will prove very useful when we run into an annoying error later on. SNS and SES also are given permissions so we can send our messages.
Create the State Machine
The state machine is a basic decision tree. We get our preference choice from our frontend based on the user's decision. We connect our state machine to the email Lambda function with the ARN (Amazon Resource Name) address. The ARN is a unique identifier that helps us select a specific resource.
Our state machine can now pass information back and forth with our Lambda function.
Create API and Lambda Function to communicate with Frontend User Response
The next Lambda function will help connect our API and State Machine. The information flow will occur like this:
User enters information -> Frontend posts info to API -> API runs the Lambda function -> Triggers the state machine.
This now provides all the appropriate information needed to send the correct messaging along. I created the Lambda function along with an IAM role so it could communicate with the API.
Then I configured a REST API with a post method and made sure to enable CORs. Not enabling CORs is a common mistake that will throw errors often. I deployed the API and copied the endpoint to place inside my JavaScript so the Frontend could post to the API.
Creating the Frontend
This was the most straightforward part of the process for me. I have used S3 quite a bit in the past to do static website hosting. I configured the S3 bucket to be public, uploaded the necessary HTML, CSS, and JavaScript files, and updated the JS files with the API endpoint and we were good to go!
Errors!!
I learned a lot in this process...but I learned the most from debugging the application. Finding when and where errors occur really helps gain a deeper understanding of how the services interact.
Error #1 Failure to Fetch
This immediately made me look into the API, Lambda Function, and the Javascript Code. Since it's not fetching correctly, maybe there was an error in the Javascript. I couldn't find anything there. I went on to the API and made sure I had the CORs header enabled. Everything seemed to be working fine there. The Lambda function wasn't loading the correct information, so that's why the error showed the Lambda line of code.
After all the time spent spinning in circles, I found the simple cause of the error. There was an additional space from when I copied the API endpoint address into the Javascript code. As you can see below, it can be a hard error to spot.
var API_Endpoint = "myapiendpoint.amazon.aws.com/test "
It's easy to say that was embarrassing, but I indirectly learned a ton through the debugging process. If I caught the error initially, I would have saved a lot of time, but I wouldn't have dug into how all these services interact. Although it was frustrating, in retrospect I gained a deeper understanding of these AWS services.
Error #2 Invalid ARN
This was another simple error, and this only showed up after I fixed the first one. The reason being is that the information never loaded correctly due to the first error, so it never got triggered until after the fact.
The ARN I had in the Lambda handler looked correct, but when I went to double-check I noticed that I copy/pasted the ARN from the IAM role (located below the ARN of the state machine), instead of the actual ARN of the state machine itself. Another simple mistake, but this one only took me about 30 seconds to fix.
After that, I had the application up and running. I tested the application by sending an email and text...and success!!!
And the email below:
Takeaways
It felt good to work through the small errors I encountered while deploying this application. I dread running into random bugs, but I always have a sense of accomplishment and understanding after the fact. It always ends up being a great use of time because I learn so much faster this way.
This project definitely reinforced my knowledge of Lambda functions. They are an extremely useful, versatile resource in AWS. Building out API's is always a very fun thing to do...once you get them working. I have worked with the majority of these services before in different workflows. It was great to gain experience working with multiple Lambda functions.
The step function was a whole new experience. I love the visual flow chart for the input preferences. Any time you get a chance to visualize code, it really helps with understanding the fundamentals behind it.
Configuring any backend can be difficult, but with time and patience, you can create a well-designed framework that can scale when needed. These are great skills to have!
Follow
- Thanks to Adrian Cantrill for the great demos and learning materials!
- Follow me on Twitter
- Connect with me on LinkedIn