Flask is a lightweight Python web framework. It’s lightweight nature makes it a great candidate for creating APIs. In this tutorial, I’m going to cover how to make a simple RESTful API service with Flask and host it on AWS.

First, we’ll make a basic Flask app and use AWS Elastic Beanstalk to deploy it. Then, we’ll use Zappa to deploy a serverless app to AWS Lambda. For more information on serverless architectures and how Zappa works, check out the About section on their GitHub page.

Python Version

Since the goal is to make an AWS-hosted API, we’ll need to make sure the version of Python we use is supported by AWS and the other tools we’ll be using.

Supported Python Runtimes on AWS Elastic Beanstalk
Supported Python Runtimes on AWS Lambda
Supported Python Runtimes for Zappa

Currently, the latest version of Python supported by all 3 of the above is 3.8. So please make sure Python 3.8 is installed before proceeding.

Setup

As best practice, we’ll be using a virtual environment for managing dependencies. This will also be required by Zappa later on.

Start by creating a folder for the project. I’m going to be calling it simple-flask-app. Open a terminal console in the folder.

If you only have Python 3.8 installed, you can create a virtual environment with the following command:

virtualenv env

If you have multiple Python versions installed, you’ll need to specify which version to use:

virtualenv --python 3.8 env

env is the name of the environment. You can name it whatever you like. Just make sure to add the folder with this name to your .gitignore if you’re using Git.

To activate the virtual environment, enter the following command:

source env/Scripts/activate

Now that we’ve got our virtual environment activated, we can install our dependencies.

pip install flask

It’s also a good idea to maintain an up-to-date list of project dependencies.

pip freeze > requirements.txt

This can be used by someone to setup their environment when trying to develop/run the application.

Flask App

The main file will be called application.py and the Flask callable object will be named application since that is what AWS Elastic Beanstalk will look for when it runs the app.

from flask import Flask

application = Flask(__name__)

@application.route("/")
def hello():
    return "Hello, World!"

@application.route("/send", methods=["POST"])
def send():
    return "Success"

We’ll need to set some environment variables that Flask will use.

export FLASK_ENV=development
export FLASK_APP=application

FLASK_ENV tells Flask to run in development mode. This allows debug output when we inevitably encounter errors and a few other neat features. FLASK_APP tells Flask the name of the main file (the .py extension is implicit).

We can now run the application.

flask run

The application should now be running on localhost:5000.

Let’s test if it’s working. In a new terminal window (so as to not kill the running Flask app), send a request to the app with curl.

curl localhost:5000/

You should see a “Hello, World!” response. This is our response to the / route.

Now, let’s test the /send route.

curl localhost:5000/send

You should have received an error response. Since the only method allowed for the /send route is POST and curl is sending a GET request, Flask creates an HTML page to display the error response and returns that.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>405 Method Not Allowed</title>
<h1>Method Not Allowed</h1>
<p>The method is not allowed for the requested URL.</p>

So, let’s try again. But this time, we’ll tell curl to use the POST method.

curl -X POST localhost:5000/send

Now we should see our success message.

We can even send data to the server. Let’s make a slight modification to our send() function.

@application.route("/send", methods=["POST"])
def send():
    return "Success. Message: " + request.form['message']

Saving the file should automatically refresh the Flask app (another perk of development mode).

Now we can send some data to the app.

curl -X POST localhost:5000/send -d "message=testing"

We should see our new success message that parrots back the data we sent.

Albeit a basic one, this is the Flask app we will now be deploying to AWS.

AWS Elastic Beanstalk

We’re going to use Elastic Beanstalk (EB) to deploy our app. EB allows for quick and easy deployment while letting AWS configure and manage the underlying infrastructure required to run the application. For this section, make sure you have the EB CLI installed.
We can then run the following commands to deploy to AWS EB (on Windows, these will need to be run from Powershell).

eb init -i
eb create simple-flask-app

The first command configures the application. For the most part, you can go ahead with the defaults. I generally opt-out of using CodeCommit as I prefer to use GitHub. It’s a good idea to use SSH. The second command creates the environment in EB and deploys the contents of the working directory. simple-flask-app is the name of the environment. You can name it whatever you like as long as there is no other environment in your account with the same name.

You can view the running instance with the following command.

eb open

Copy the URL from the browser window that just opened. Let’s use curl to send requests to the deployed instance to see if everything is working correctly.

curl <EB_URL>
curl -X POST <EB_URL>/send -d "message=testing"

You should receive the same success messages that were received when running locally.

If changes are made to the application, the deployment can be updated with the following command.

eb deploy

Once we’re done using the EB deployment, we can destroy the instance with the following command.

eb terminate simple-flask-app

AWS Lambda

Deploying to AWS Lambda is even easier, thanks to Zappa!

pip install zappa

As of this writing, there is an open issue on Zappa’s GitHub about the error you’re going to encounter: Zappa and Flask require conflicting versions of a common dependency, Werkzeug. There’s also another issue that I ran into. One of Zappa’s dependencies, troposphere, made a breaking change. The issue was recently closed on GitHub but hasn’t made it to the latest release yet.

To fix both these issues, run the following commands.

pip install Werkzeug --upgrade
pip install troposphere==2.7.1

After this, we should update our requirements.txt file to keep our dependency list up-to-date.

pip freeze > requirements.txt

Now we’re ready to deploy to AWS Lambda.

zappa init
zappa deploy

Zappa will take care of all the AWS configuration and setup for us. At the end, you should see a URL given where you can access the Lambda functions.

Let’s try it out in curl.

curl <LAMBDA_URL>
curl -X POST <LAMBDA_URL>/send -d "message=testing"

And again, you should receive the same success messages that were received when running locally and on EB.

Once we’re done using the Lambda functions, we can undeploy the app with the following command.

zappa undeploy

Conclusion

The full code is accessible on GitHub.

To sum up, we were able to create a Flask application and deploy to AWS Elastic Beanstalk and to AWS Lambda.

You may want to use EB when you need a traditional web service model. Perhaps your workloads are more or less constant, you need advanced monitoring, or have long-running functions.

The serverless model with Lambda may be ideal if you need greater scalability, minimal maintenance, and cheaper hosting expenses.