Rest API development with Python, Flask, MongoDB, Docker | Part 2

Adnan Kaya
4 min readJan 7, 2023

--

In the previous tutorial we did set up the project and wrote our first endpoint and view.

You can clone down the project from my github account.

Today we are going to dockerize our project and add new models. Let’s get started!

We are going to start by writing our docker-compose.yml file.

# Flask_MongoDB_Docker/docker-compose.yml

version: '3.7'
services:

db:
image: mongo:latest
container_name: mymongodb
ports:
- 27017:27017
volumes:
- mongo_data:/data/db

volumes:
mongo_data:

We only added db service which uses mongo’s latest image and container is named as mymongodb (pay attention this container name because when we add the flask(web) service we will use this container name to connect from flask container to mongo container). Lastly we named volume as mongo_data. Let’s start up the mongodb container by writing the following command:

$ docker compose up -d --build db
# db is our mongodb service name which is specified in docker-compose.yml

# output
[+] Running 2/2
⠿ Network flask_mongodb_docker_default Created 0.0s
⠿ Container mymongodb Started

# we can check our container status
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
dfa10167aa02 mongo:latest "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 0.0.0.0:27017->27017/tcp mymongodb

Let’s take a look our .env file

FLASK_APP=main.py
FLASK_DEBUG=1
MONGODB_DBNAME=mydb
MONGODB_HOST=localhost
MONGODB_PORT=27017
MONGODB_USERNAME=developer
MONGODB_PASSWORD=developer

Currently we only have 1 container which is mongodb. We are going to run flask from our terminal. Because of we connect to mongodb from flask directly(not from any container), we will use MONGODB_HOST as localhost. When we start up flask as MONGODB_HOST value will be mongodb service’s container name. Let’s run the flask

$ source env311/bin/activate
(env311)$ flask run

Make a GET request to http://127.0.0.1:5000/api/v1/books/

You should get empty list [ ] response with HTTP Status 200. But we had inserted 3 records in the previous tutorial. Where is our data ?

We did not mount our container data in the previous tutorial. That’s why when our previous container got removed we lost our 3 records. In this tutorial in the docker-compose.yml file we specified our volume to persist our data even our container gets removed.

Now let’s dockerize flask and connect to mongodb container. Open up the docker-compose-yml file and add web to the services.

# Flask_MongoDB_Docker/docker-compose.yml
version: '3.7'
services:
web:
container_name: flask_mongodb_docker_tutorial
build:
context: .
dockerfile: ./Dockerfile
volumes:
- .:/usr/src/app/
ports:
- 5000:5000
env_file:
- ./.env
depends_on:
- db
stdin_open: true
tty: true

db:
image: mongo:latest
container_name: mymongodb
ports:
- 27017:27017
volumes:
- mongo_data:/data/db

volumes:
mongo_data:

Now edit .env file like the following

FLASK_APP=main.py
FLASK_DEBUG=1
MONGODB_DBNAME=mydb
# MONGODB_HOST=localhost
# when using docker compose we need to specify as MONGODB_HOST=container_name
MONGODB_HOST=mymongodb
MONGODB_PORT=27017
MONGODB_USERNAME=developer
MONGODB_PASSWORD=developer

Now we will edit our Dockerfile

# Flask_MongoDB_Docker/Dockerfile

# pull official base image
FROM python:3.11.0-slim-buster

# set work directory
WORKDIR /usr/src/app

# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

# install system dependencies
RUN apt-get update && apt-get install -y netcat

# install dependencies
RUN pip install --upgrade pip
COPY ./requirements.txt /usr/src/app/requirements.txt
RUN pip install -r requirements.txt

# copy project
COPY . /usr/src/app/

# run
CMD ["python", "-m", "flask", "run", "--host=0.0.0.0", "--port=5000"]

I commented out for the docker commands. You can figure out easily.

Now stop the running flask on the terminal. And build flask(web) service

$ docker compose up -d --build web

# output
[+] Building 2.6s (12/12) FINISHED
# ...
# .....
[+] Running 2/2
⠿ Container mymongodb Running 0.0s
⠿ Container flask_mongodb_docker_tutorial Started

# checking the containers status
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ccd7f1c0de54 flask_mongodb_docker-web "python -m flask run…" 4 minutes ago Up 3 minutes 0.0.0.0:5000->5000/tcp flask_mongodb_docker_tutorial
dfa10167aa02 mongo:latest "docker-entrypoint.s…" 37 minutes ago Up 37 minutes 0.0.0.0:27017->27017/tcp mymongodb

Now you can make a GET request http://127.0.0.1:5000/api/v1/books/ and get response empty list and status as 200.

Before adding new records, lets edit models.py and add new model to make a relationship between models.

# Flask_MongoDB_Docker/api/models.py
# internals
from api import db


class Author(db.Document):
firstname = db.StringField(max_length=64)
lastname = db.StringField(max_length=64)

def __str__(self):
return f"{self.firstname} {self.lastname}"


class Book(db.Document):
name = db.StringField(max_length=64)
author = db.ReferenceField(document_type=Author)
published_year = db.IntField(min=610)

def __str__(self) -> str:
return self.name

Every book has an author. Simple relationship (One-to-Many)

Let’s create author and book records on flask shell. Firstly we need to go into flask container.

$ docker compose exec web sh
# ls
Dockerfile README.md __pycache__ api config.py docker-compose.yml env311 main.py requirements.txt
# pwd
/usr/src/app

# flask shell
Python 3.11.0 (main, Dec 6 2022, 08:43:25) [GCC 8.3.0] on linux
App: api.app
Instance: /usr/src/app/instance
>>>
>>> from api.models import Author, Book
>>> a1 = Author.objects.create(firstname="Adnan", lastname="Kaya")
>>> a2 = Author.objects.create(firstname="Recep", lastname="Alkurt")
>>> a1
<Author: Adnan Kaya>
>>> a2
<Author: Recep Alkurt>
>>> b1 = Book.objects.create(author=a1, name="Flask 101", published_year=2023)
>>> b2 = Book.objects.create(author=a2, name="Angular 101", published_year=2023)
>>> b3 = Book.objects.create(author=a1, name="Django 101", published_year=2025)
>>> b4 = Book.objects.create(author=a1, name="Scrapy 101", published_year=2024)
>>> b1
<Book: Flask 101>
>>> b1.author
<Author: Adnan Kaya>
>>> b2
<Book: Angular 101>
>>> b2.author
<Author: Recep Alkurt>

GREAT! We inserted 2 authors and 4 books into the database.

Let’s take a look our views.py (We did not change anything so far)

from flask import Blueprint, jsonify
# internals
from .models import Book

books_bp = Blueprint("books", __name__)


@books_bp.route("/books/", methods=["GET"])
def books():
book_list = Book.objects.all()

return jsonify(book_list)

Make a GET http://127.0.0.1:5000/api/v1/books/ the response will be

[
{
"_id": {
"$oid": "63b9d62bd1627fb9544b8833"
},
"author": {
"$oid": "63b9d5ded1627fb9544b8831"
},
"name": "Flask 101",
"published_year": 2023
},
{
"_id": {
"$oid": "63b9d63dd1627fb9544b8834"
},
"author": {
"$oid": "63b9d601d1627fb9544b8832"
},
"name": "Angular 101",
"published_year": 2023
},
{
"_id": {
"$oid": "63b9d64dd1627fb9544b8835"
},
"author": {
"$oid": "63b9d5ded1627fb9544b8831"
},
"name": "Django 101",
"published_year": 2025
},
{
"_id": {
"$oid": "63b9d65cd1627fb9544b8836"
},
"author": {
"$oid": "63b9d5ded1627fb9544b8831"
},
"name": "Scrapy 101",
"published_year": 2024
}
]

It works fine :) . You can access the github repo in here.

You can read previous tutorial here.

If you want to read some information about me and contact visit my github page: https://adnankaya.github.io/

--

--