Create a Flask REST API running on AWS Elastic Beanstalk
Deploy a REST API running on Flask to AWS Elastic Beanstalk, with automatic SSL certificate provisioning via Let's Encrypt
This post explains how to set up a Flask app serving as a starting point for building a REST API. I’ve compiled the necessary steps to successfully deploy the API via AWS Elastic Beanstalk, including configuration of SSL certificates via Let’s Encrypt.
We’ll use…
- Python 3 and PyCharm as an IDE
- AWS Elastic Beanstalk to host our API
- Flask, a popular Python micro framework for the web
- PostgreSQL as a database (hosted remotely on AWS RDS)
- Let’s Encrypt for SSL encryption
- Gunicorn (WSGI server)
The result is a fully working REST API serving as a starting point for your project (for example, a microservice).
Project Setup and Dependencies
In this step we will install all requirements on our local machine. I’m using macOS 10.12 and Python 3.6 - the commands might not correspond exactly to your environment, so be sure to watch out for that.
Important: While debugging, when serving the API or when you install additional dependencies below using pip, make sure you switch to the virtual environment accordingly:
Open the project folder in your code editor, e.g., VS Code or PyCharm.
To get started, add the following barebone code to create the first route:
Prepare the WSGI server (we’re using gunicorn):
You can make sure that everything is working properly by directly executing the Flask app:
Alternatively, run gunicorn to serve your app:
Press CTRL+C to exit the respective process. By default, Flask RestPlus provides Swagger UI documentation, served from the root of the API. After accessing the root URL (see above, for example http://0.0.0.0:8080
) from your browser, you’ll be presented with the automatically-generated Swagger UI documentation, in which you can interact with the API (using the “Try it out!” button):
Try calling the API endpoint from Terminal (make sure to open a separate window in Terminal, in order not to interrupt the current process):
You can see that upon a GET request, our endpoint /hello
will output the expected response from app.py
, conveniently formatted in JSON:
Our API is now working with a minimal “hello world” scenario and we are ready for the next step, where we will implement another endpoint which will communicate with our PostgreSQL database.
Database Integration
Normally, an API needs to perform operations on a database (i.e. create, read, update and delete, see CRUD). That’s why we will connect our API to a database, in our case a remote PostgreSQL database hosted on AWS RDS.
In our PostgreSQL database, we’ll create a simple table called blog_posts
for our purposes by running the following SQL code:
First, let’s set up database handling:
Then, let’s set up a model to reflect our database schema:
In the main Flask app file, we need to add new import statements and an additional method, which will retrieve all blog posts from the table. Change the code accordingly:
Now, serve the API as before by typing the following command in the console, for example with gunicorn:
Try sending a request to the endpoint we just created from a separate Terminal window:
You can see that upon a GET request, our endpoint /blog_posts
will query the database and return the blog posts in JSON format:
We successfully connected to our database. Let’s proceed to the next step, where we will prepare our app for deployment to AWS Elastic Beanstalk.
Deployment to AWS Elastic Beanstalk
In order to deploy our API to AWS Elastic Beanstalk, we first need to install the AWS Elastic Beanstalk command line interface. I recommend installation using brew, which handles all dependencies correctly. If you have it already installed on your machine, you can skip this step. Run this command in order to install the CLI:
You can check if the installation was successful by executing:
Now, execute the following in your project folder to initialize the application and register it with AWS Elastic Beanstalk (be sure to change the region accordingly; below, eu-west-1 is selected):
Note: Python 3.4 is the latest version available at the time of writing.
This will trigger prompts which will help you to set up the application. You will need to enter your AWS credentials, namely aws-access-id and aws-secret-key.
In order to install PostgreSQL on the EC2 instances where our API will be deployed, an additional step is required. Paste the following in Terminal to add additional dependency installation instructions to the corresponding file to enable PostgreSQL support in AWS Elastic Beanstalk environments (the file is located at .ebextensions/packages.config
):
The following command will create an environment (called testenv-barebone-flask-rest-api
), provision the necessary instance (we’ll use a single instance setup without load balancer) and deploy the application:
This will take a few minutes to complete. Note that at this point, the application will not work yet, because the default WSGI-Path configured by AWS Elastic Beanstalk is application.py
, whereas in our project it is wsgi.py
.
In order to tell AWS Elastic Beanstalk to set up the EC2 instances with the correct WSGI-Path (where our API should ultimately be served from), we have to adjust the configuration:
Change application.py
to wsgi.py
(see below). This will ensure that the correct file from our project folder is referenced (in other words, defining wsgi.py
as the entry point to our application).
Exit and confirm the changes. This will subsequently deploy the new configuration to your EC2 instance (i.e., replace the current instance).
After deployment of the new configuration (which takes another 2–3 minutes) you can type the following command to open the URL under which your API is served from - and confirm that everything is working as it should:
You can also retrieve the CNAME (alongside other useful information about your environment) like so:
You can see that the two methods we’ve implemented are working properly by calling them from your Terminal or browser. Alternatively, you can test the endpoints using a API client like Postman or Paw – especially when implementing more methods this should speed up your development and debugging.
If you make changes to the source code of your API, you can re-deploy it to AWS Elastic Beanstalk via the following command:
SSL Configuration via Let’s Encrypt
The steps below will show you how to configure an SSL certificate without additional charges, using Let’s Encrypt. First, you’ll need to create a hosted zone for your custom domain in Route 53. Then, you can attach a CNAME Alias in the hosted zone, pointing to your AWS Elastic Beanstalk deployment. See this AWS guide for more information.
Note: The next step will download a config file from gist.github.com. Be sure to check out the source code before pasting the command into your command line!
After you verify that the Route 53 DNS changes have propagated successfully, run the following commands in Terminal in the root of your project (be sure to replace the two environment variables with your email, as well as your domain name configured in Route 53 from the step above):
The config script above will install a cronjob to make sure that your SSL certificates are periodically renewed. Now, your API is served via AWS Elastic Beanstalk, accessible on your custom domain, and secured via SSL provided by Let’s Encrypt.
I hope you enjoyed this article - please let me know if you have any questions or if you run into any issues. Thanks for reading!