How to Build Python Flask RestAPI with Postgres and Dockerize It on Google Kubernetes using HELM - Part 1

·

4 min read

Introduction

RestAPI is very popular. I recently learned some Python tech and how to build RestAPI using Flask Python, here I will show you the details.

And I will use the PostgreSQL database as the data source for RestAPI, and my last article described how to Install PostgreSQL Docker on Oracle Cloud.

Also, I like Docker very much, here I will install RestAPI into docker and deploy it to Google Kubernetes using HELM.

I really enjoyed the hands-on process :)

Build Python Flask RestAPI with Postgres

Database

Create sequence, table and inserting data in the PostgreSQL database

CREATE SEQUENCE IF NOT EXISTS public.tasks_seq
    INCREMENT 1
    START 1
    MINVALUE 1
    MAXVALUE 9223372036854775807
    CACHE 1;

CREATE TABLE IF NOT EXISTS public.tasks
(
    id          integer NOT NULL DEFAULT nextval('tasks_seq'::regclass),
    name        character varying(20) COLLATE pg_catalog."default",
    description character varying(200) COLLATE pg_catalog."default",
    CONSTRAINT tasks_pkey PRIMARY KEY (id)
);

insert into tasks(name,description) values('task1','this is task 1');
insert into tasks(name,description) values('task2','this is task 2');
insert into tasks(name,description) values('task3','this is task 3');

select * from tasks;

image.png

Using Example Project

Here is an example project on how to develop RESTful API with Flask and Python

Clone the project to your local

git clone https://github.com/bbachi/python-flask-restapi.git

Under the project, modify the requirements.txt under the project like this:

flask
psycopg2-binary
Flask-SQLAlchemy
python-dotenv

Install the packages in the requirements.txt

python3 -m pip install -r requirements.txt

Run Flask and check if it works

flask run

The VS Code terminal shows:

image.png

Open the browser and check if it works

http://127.0.0.1:5000/api/tasks

image.png

Coding

Keep the project files like this:

image.png

Modify the app.py

My codes implements receiving HTTP requests for RESTful Services and performing CRUD operations on the Postgres database

import os
import json
from flask import Flask, request
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

# app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL')

# XXX.XXX.XXX.XXX is your PostgreSQL database IP
app.config['SQLALCHEMY_DATABASE_URI']='postgresql://postgres:postgres@XXX.XXX.XXX.XXX:5432/demo'

db = SQLAlchemy(app)

class Task(db.Model):

    __tablename__ = 'tasks'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String())
    description = db.Column(db.String())

    def __init__(self, name, description):
        self.name = name
        self.description = description

    def __repr__(self):
        return '<id {}>'.format(self.id)

    def serialize(self):
        return {
            'id': self.id, 
            'name': self.name,
            'description': self.description
        }

@app.route('/')
def home():
    return "RestAPI Demo!"

@app.route('/api/tasks')
def tasks():
    try:
        tasks=Task.query.all()
        return  json.dumps([e.serialize() for e in tasks])
    except Exception as e:
        return(str(e))

@app.route('/api/task', methods=['POST'])
def create_task():
    try:
        body = request.get_json()
        task = Task(body['name'], body['description'])
        db.session.add(task)
        db.session.commit()
        return "Task added. task id={}".format(task.id) 
    except Exception as e:
        return(str(e))

@app.route('/api/task/<int:id>', methods=['PUT'])
def update_task(id):
    try:
        body = request.get_json()
        db.session.query(Task).filter_by(id=id).update(
            dict(name=body['name'], description=body['description'])
        )
        db.session.commit()
        return "Task "+str(id)+" updated"
    except Exception as e:
        return(str(e))

@app.route('/api/task/<int:id>', methods=['DELETE'])
def delete_task(id):
    try:
        #Task.query.filter_by(id=id).delete()
        db.session.query(Task).filter_by(id=id).delete()
        db.session.commit()
        return "Task "+str(id)+" deleted"
    except Exception as e:
        return(str(e))

Run Flask again

flask run

Open the browser and check if it works

http://127.0.0.1:5000/api/tasks

image.png

It works and prove that the Python program has successfully fetched the data from the Postgres database.

Dockerize the RestAPI

In order to put the database connection information in the system environment variable and comment it out from the program, modify the app.py section like this

app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL')

# XXX.XXX.XXX.XXX is your PostgreSQL database IP
# app.config['SQLALCHEMY_DATABASE_URI']='postgresql://postgres:postgres@XXX.XXX.XXX.XXX:5432/demo'

And modify the Dockfile like this

FROM python:3.11

WORKDIR /opt/app

COPY . .

# XXX.XXX.XXX.XXX is your PostgreSQL database IP
ENV DATABASE_URL="postgresql://postgres:postgres@XXX.XXX.XXX.XXX:5432/demo"

RUN pip install --no-cache-dir -r requirements-prod.txt

EXPOSE 8080

CMD ["python3", "-m", "flask", "run", "--host=0.0.0.0", "--port=8080"]

Download and install Docker

image.png

Docker build

docker build -t pythonrestapi .

image.png

Show docker images

docker images

image.png

Run docker

docker run -d -p 8080:8080 --name pythonrestapi pythonrestapi

docker ps -l

image.png

Open the browser and it also works

http://127.0.0.1:8080/api/tasks

image.png

Great!

At this point, I have successfully built a Rest API docker image and it runs looking good.

Next, I will try to deploy this image to Google Cloud.

Here the Part 2 - How to Build Python Flask RestAPI with Postgres and Dockerize It on Google Kubernetes using HELM - Part 2