FastAPI Email Service with Python Boto3, Amazon SES and Elastic Container Registry (ECR)

Azzan Amin
TheLorry Data, Tech & Product
11 min readOct 15, 2021

Let’s build a FastAPI Email Service with SES, dockerize it, and push it to ECR!

Photo by Mathyas Kurmann on Unsplash

Building a structured in-house email server in your company is a great way to reassess your email-sending strategy. Good email deliverability is of the utmost importance when maintaining a good reputation with the Email Service Provider (ESP) and it requires us to send high-quality emails. Email deliverability is referring to the percentage of emails that arrive in our recipients’ inboxes which means to say that our recipient finds our email valuable and not label them as spam.

When you send high-quality content, your reputation becomes more trusted over time and Amazon SES increases your sending quotas. — Amazon SES

In this article, we will demonstrate how to create a Simple Email Service API using FastAPI and Amazon SES as the ESP. Once the API is created, we will then dockerize the API and push it to a Docker Container Registry which we will be using Amazon Elastic Container Registry. (ECR).

Without further ado, let’s go!

Table of Contents

  1. The Prerequisites
  2. Building the FastAPI Application
  3. Dockerize the FastAPI
  4. Register Docker Image to ECR
  5. Pull and Run the ECR Image

The Prerequisites

Before we begin, we need to perform these following tasks first:

  1. Create an AWS Account How do I create and activate a new AWS account?
  2. Get the AWS Credentials Understanding and getting your credentials
  3. Install Python 3Download and Install Python

Building the FastAPI Email Service Application

1. Setup Python Virtual Environment

Once we have Python 3 installed, create a virtual environment inside our local project directory. Open a terminal on our local machine and run the following commands:

Install the virtualenv package

We can install the package by using pip.

pip install virtualenv

Create the virtual environment

To create a virtual environment, we must specify a path for that. For example to create one in the local directory called venv, type the following:

virtualenv venv

Activate the virtual environment

We can activate the python virtualenv by running the following command:

  • Mac OS / Linux
source venv/bin/activate
  • Windows
venv\Scripts\activate

2. Install Dependencies

Before we start to code, we need to install these libraries first:

Install FastAPI — modern, fast (high-performance), web framework for building APIs with Python 3.6+ based on standard Python type hints.

pip install fastapi

Install Boto3 — An AWS SDK for Python that is used to create, configure, and manage AWS services

pip install boto3

Install aiofiles — Handling files in asynchronous application

pip install aiofiles

Install python-multipart — To handle form encoded data and file uploads

pip install python-multipart

Install uvicorn — An ASGI server to run our FastAPI app.

pip install uvicorn

Nice, all libraries needed has been installed. Let’s code them!

3. Understanding The File Structure

The file structure that will be using for this project.

./
├─ Dockerfile
├─ requirements.txt/
└── app/
├─ __init__.py/
├─ main.py
├─ utils/
│ ├─ __init__.py/
│ ├─ mail_utils.py
│ └── utils.py
├─ model/
│ ├─ __init__.py/
│ └── emails.py
├─ data/
│ └── temp/
└── api/
├─ __init__.py
└── api_v1/
├─ __init__.py
└── endpoints/
├─ __init__.py
└── services.py

Based on the structure above, every folder are containing an empty __init__.py . It means that they are treated as python modules. The app directory contains all the modules and it is considered as a Python Package which is referring to a collection of Python Modules.

4. The Utilities

Let’s start with creating utility files located in the moduleapp.utils. The file path isapp/utils . In the utils module, TWO utils files are created named mail_utils.py and utils.py .

mail_utils.py

There are TWO methods defined in mail_utils.py:

create_email_multipart_message() : This method will creates a MIME multipart message object.

send_mail() : A method that will perform email sending requests to Amazon SES using boto3 client. It will call the create_email_multipart_message() to create the multipart message object first and then send an email to Amazon SES.

utils.py

The utils.py consists of FIVE utils methods and one of the important methods is the execute_multipart_emailing_service() . This method will perform these processes:

  1. Save the uploaded files to the project temporary data folder.
  2. Send the email request to send_mail() method.
  3. Remove the files saved in the temporary data folder.
  4. Return the email sending status.

As mentioned above, we have a data folder, app/data/temp that will store the attachment files temporarily. You can refer to utils.py above where we have an async function called save_uploaded_files_to_wkdir() to store the files in the temporary folder and remove_directory() is a function to remove the directory of the specified path.

5. The Model

We have ONE model file called emails.py which is located in the module app.model.emails . The file path: app/model/emails.py .

emails.py

In this model file, we have one decorator function which is called asas_form that will convert a pydantic BaseModel class into FastAPI Form format. In line 43, we can see that the @as_form is being placed right before defining the EmailRequest class. Thus, we will set all the fields in that class into Form format. By the way, there is only one class which is the EmailRequest and all the required fields for sending emails will be defined there.

6. The API Routers

All the FastAPI routers are placed in the app.api module. In that module, we have few more submodules that will define the api version, for instance, app.api.api_v1 and the endpoints app.api.api_v1.endpoints .

./
└── app/
└── api/
├─ __init__.py
└── api_v1/
├─ __init__.py
├─ api.py
└── endpoints/
├─ __init__.py
└── services.py

First, we need to define our API endpoints in app/api/api_v1/endpoints/services.py .

services.py

Then, we need to create api.py in the app.api_v1 module.

api.py

7. The Main Application

Lastly, create theapp/main.py file which consists the FastAPI app object.

main.py

In line 23, we can see that the router object which is called api_router has been included in order to access the endpoint resources that we have defined.

Phew, everything is ready!

Alright, let’s make this FastAPI app up and running.

8. Amazon SES — The Sandbox Mode

Before testing the endpoint, make sure that we have added or verified few email addresses in Amazon SES if your SES service is still in Sandbox mode. Otherwise, we will not be able to send the email through the API.

Verify Email Address in Amazon SES

Why Sandbox Mode?

AWS place all the new accounts in the Amazon SES sandbox in order to prevent fraud and abuse, and help to protect the sender’s reputation when using the service.

There are some restrictions applied to the account if it is in the sandbox. These are the following restrictions:

  • A maximum of 200 emails can be sent per 24-hour period.
  • Only can send 1 email per second.
  • Email can only be sent to verified emails/identities in Amazon SES.

If you want to move out your account from Sandbox Mode, please refer to the documentation below.

If you have been granted production access, the default quota for sending emails via Amazon SES is 10,000 per day. Amazon SES will increase your current quota limit by itself when a large number of high-quality emails are being shot every day. Your quota will be modified by Amazon SES as per your requirements automatically which means that you don’t need to send any new request to Amazon SES. Remember, this only applies when your email deliverability reputation with Amazon SES is good.

9. Test the API

We can try to run our FastAPI locally by typing the command below at the root of the project.

unicorn app.main:app --reload
FastAPI up and running

In order to check whether the API is working, we can try to open http://localhost:8000/docs to see the API documention and then you can try it out.

Amazing (^-^)

For those who are still using the Amazon SES sandbox, please ensure all the email addresses that we want to use are already verified before sending the email through the API. This is the response if the unverified emails being sent to the API POST request:

But if you already verified them, try to send the email through the API endpoint. For a successful POST request, you will receive a response like this:

If we are not adding any attachment files, please make sure we untick the send empty value like this:

Otherwise, the response that we get will be like this.

Then, we can try to check our email whether we receive it or not. Kindly check in your junk email if you do not find it in your email inbox. We can now focus on dockerizing our FastAPI and push it to Amazon ECR.

Let’s dive in!

Dockerize the FastAPI

Alright, let’s create our Dockerfile. We need to start by building our FastAPI image by referencing the path to the following Dockerfile:

Let’s breakdown the instructions in the above Dockerfile:

  • Line 1: The base image used is python:3.9-slim-buster which is one of the reliable choices for building a python image with minimum packages to run python.
  • Line 3: Define work directory of a Docker Container named /app
  • Line 5: Copy requirements.txt file from the project directory to /app work directory of the docker container.
  • Line 7: Install and upgrade python pip, setuptools and wheel packages
  • Line 9: Install all the python dependencies stated in the requirements.txt file
  • Line 11: Copy all the files and folders in the root of the local project directory to the /app work directory of the docker container.
  • Line 13: Define the command to be executed in order to run the FastAPI app.

Alright, now let’s build the docker image. Run the following command in the terminal to build the docker image named email-service:

docker build -t email-service .

Register Docker Image to Amazon ECR

Once the docker image is successfully built, you can push your image to Amazon ECR. Before you push it, please make sure that you create the private repository in Amazon ECR first.

Go to AWS Management Console and log in using our AWS Account. Then, proceed to Amazon Elastic Container Registry in the console and Click on Create repository.

Create Private Repository

Setup the repository settings as per the image below. Set the repository name as email-service and click on Create repository.

Done! we can see now the ECR repository has been successfully created.

That is all we need to create a private ECR repository. Now, we can tag and push our image to ECR. Select our created repository ( email-service ) and click on View push commands. You will see a modal pop up as shown below.

Copy all the commands above in your terminal to tag and push your image to ECR. If you noticed, we already did the second command which is building the image. Therefore, we can skip the command if we want.

Once the image is successfully pushed, we will see a result like this in our ECR.

Awesome, Our app is now on AWS!

Pull and Run the ECR Image

Okay, let’s run our ECR image. First, we need to pull the image from Amazon ECR. Copy the ECR Image URI.

Then, run the command below at your project terminal.

docker pull <your-image-uri>

Once the pulling request is succeeded, run to spin up the docker image.

docker run -dp 80:80 <your-image-uri>

Great! Now our docker container is running. We can verify it through our Docker Desktop.

We can check our FastAPI Docs at localhost/docs

Voila! Everything is up and running

Congratulations! We have reached the end of this walkthrough. Thank you for reading.

Summary

The FastAPI Email Service that we have build earlier only performs the email sending part. To create a better-structured email server application, we will need to add more layers of security and process before sending the email. for instance adding API Key authentication for each user that uses the API, removing blacklisted email, filtering email bounces, storing the email sending logs and fail logs in the database, and a lot more.

AWS encourages us to understand the email delivery issues, be proactive to prevent them, keep on monitoring the email status that we send and then improve our email-sending program. These proactive actions could lead us to a better reputation with Amazon SES, larger sending quotas, higher email quality being sent to the recipients, and further increase the likelihood of successful deliveries.

We hope this article will help you to start building your own awesome email server project using FastAPI and deployed in a containerized fashion.

Peace! ✌️

--

--