Indominus Tex - Serverless Pizza Tracker on AWS
tl;dr: Stack - AWS Lambda (Python 3.6), SimpleDB, Twilio, Flask, Zappa. For tracking Domino’s Pizza Malaysia orders.
Once every week or two weeks, I order pizza from Domino’s. Their website has an order tracker:
It is good enough, however after a few minutes of tracking I always encountered session expired message and the tracker stopped working. If I refresh the page, I had to log in and navigate to the tracker again, which requires more typing and clicking. And the cycle continues.
I noticed that the website does an API call every few seconds to get the order status, and decided to build my own tracker that does not need me to constantly babysit the browser session timing out. The tracker will send an SMS instead.
Previously, I did have an Ubuntu VPS instance on DigitalOcean, which was a good cheap alternative to an always-running AWS EC2, before LightSail came along. I used to have a few Django or Flask based apps running on the same VPS. Deploying a new app or updating the existing apps took too much effort, as I had to SSH in to pull a new repository, configure nginx, databases, etc. I was introduced to Fabric by this book which could make the tasks more bearable, but until today there is no official Python 3 package.
Deployment map by Full Stack Python
Directly handling servers feel like a hassle nowadays. At work, we use Docker a lot. But if I were to use it for this project, I still need to pay the always-running container instance. With the cost constraint, I decided to use AWS Lambda. In order for me to quickly develop, build and deploy my toy projects with cost in mind, going serverless via Lambda seems like a logical decision.
I have used Lambda before, but the set up was not without its own problems. In order to expose the Lambda function to the internets, AWS API Gateway has to be configured and wired up with AWS IAM role to each function. Then I discovered Zappa, a framework to package, deploy Python WSGI app on Lambda and API Gateway, without manually going through the AWS dashboard nor composing lower level operations via AWS SDK.
With Zappa as the deployer, I chose Flask as the WSGI app framework. Initially I considered Django as well, but decided against it as it seems to be a little overkill for this small project. Due to my familiarity with Django, I structured the Flask code to mimic Django, eg into Models, Controllers (Django’s Model Manager) and using Django terms such as Views.
On the storage side, SimpleDB was chosen, instead of DynamoDB, even though DynamoDB looks to be more suited and scalable for larger projects. There is flask-simple SimpleDB extension for Flask, but I decided to wrap the SimpleDB boto3 API myself to learn more about it.
To send text messages, Twilio is used. The REST API for sending SMS looks simple enough, so I opted for that instead of the official Python SDK provided by Twilio.
In this project, I used pytest for the first time, after exclusively utilising the built-in unittest module in Python standard library since I picked up Python. pytest test runner makes executing tests easier, without resorting to trial and error in finding out the correct absolute/relative import statements in test files. For this project, I still maintain the traditional jUnit (xUnit) test classes style in test files, but using pytest as the test runner.
This project utilises CircleCI for CI-CD pipeline. Initially I did not intend for this project’s repository to be publicly available on GitHub, and TravisCI has limited offering for private GitHub repositories. And since this project is now public, perhaps TravisCI could work as well as an alternative. Both of them do support definition of secret environment variables even for public projects.
For this project I also migrated DNS servers for the domain from the original registrar to AWS Route 53. This is because the registrar does not support Let’s Encrypt, and I do not want to host the project using the ugly non-human-readable pre-generated random AWS API Gateway endpoint URL. Moreover, Route 53 provides free SSL certificates via AWS Certificate Manager. It even has wildcard certificates for subdomains, which Let’s Encrypt does not have yet. Let’s Encrypt’s certificates also have to be renewed every few months.
At the time of writing, this is how the architecture looks like:
For future enhancements, maybe the architecture could be improved. Currently, most of the contents are generated and served by Flask, including the HTML responses. Next step could be that s3_website to be used to deploy static HTML contents (or even rich JavaScript SPA eg Angular client), whilst the Flask app could just serve as REST API service, passing JSON around. However this may complicate deployment as there are more moving parts, because s3_website is built on top of Ruby and Java. Although nothing is impossible if you try hard enough.
Other frameworks in the ecosystem look promising as well. Serverless focuses more on JavaScript/Node.js ecosystem, and AWS Chalice was recently released as Generally Available.
- Disclaimer: This article is not sponsored by AWS (I wish they do)
- P/S: Vendor lock-in is real. Most of my stacks are overly dependent on AWS nowadays :’(
The project can be accessed here: Indominus Tex
Project repository: codename: domini