Unlocking PostgreSQL: Connecting to FastAPI Server, Creating Tables On Ubuntu

Previously…

To deepen my understanding of PostgreSQL and Object-Relational Mappers (ORMs), I embarked on a project using PostgreSQL for manual database manipulation. This article builds on that journey by demonstrating how to connect a PostgreSQL database to a FastAPI server running on Ubuntu.

Prerequisites

This blog is a follow-up to my previous blog on how to set up PostgreSQL on Ubuntu. If you have not installed PostgreSQL on your Ubuntu you can refer to the article here before continuing. Once you’re done, you can continue with this blog:

Setting Up FastAPI

FastAPI is a lightweight and robust framework written in Python for developing APIs. It is well-known for its high performance and simplicity compared to other popular Python frameworks, such as Django. I particularly enjoy using FastAPI because it is easier to set up, and unlike Django, there is little to no abstraction involved. This allows me to have greater control over the configuration and behavior of my server.

Installation

To run FastAPI in Ubuntu is pretty easy, first open the Ubuntu desktop application you have on your system. Next, navigate to the project folder. Now you need to install python3 on Ubuntu.

sudo apt update
sudo apt install python3 python3-pip python3-venv

Now, that Python is installed successfully on your Ubuntu environment, the next will be creating the virtual environment.

python3 -m venv .venv --without-pip

This will create a .venv folder in your project.

I will get into why we used the tag —without-pip real soon.

Reason for the —without-pip attribute

Ubuntu behaves weirdly with pip. Usually when you install python3 , ensurepip Should come preinstalled as a result. It is the package that helps manage and configure pip and its site-packages behaviors, but for some weird reason, when you try to create a virtual environment without the attribute —without-pip, you will get this error:

theo@DESKTOP-Q1OABO1:/mnt/c/users/dell/desktop/projects/dummy$ python3 -m venv .venv
Error: Command '['/mnt/c/users/dell/desktop/projects/dummy/.venv/bin/python3.11', '-m', 'ensurepip', '--upgrade', '--default-pip']' returned non-zero exit status 1.

Installing ensurepip separately does not work either. To solve this issue we need to install ensurepip manually. To do this issue:

  • Create the virtual environment without pip:

      python3 -m venv .venv --without-pip
    
  • Next, navigate to the .venv folder created and activate the virtual environment:

      cd .venv
      source bin/activate
    
  • Once that is completed, we can install pip manually by sending a request to the pip set-up repository to download the configuration file:

      (.venv) theo@DESKTOP-Q1OABO1:/mnt/c/users/dell/desktop/projects/dummy/.venv$  wget https://bootstrap.pypa.io/get-pip.py
      --2024-12-31 13:09:13--  https://bootstrap.pypa.io/get-pip.py
      Resolving bootstrap.pypa.io (bootstrap.pypa.io)... 151.101.128.175, 2a04:4e42:400::175
      Connecting to bootstrap.pypa.io (bootstrap.pypa.io)|151.101.128.175|:443... connected.
      HTTP request sent, awaiting response... 200 OK
      Length: 2275758 (2.2M) [text/x-python]
      Saving to: ‘get-pip.py’
    
      get-pip.py                    100%[=================================================>]   2.17M   992KB/s    in 2.2s
    
      2024-12-31 13:09:17 (992 KB/s) - ‘get-pip.py’ saved [2275758/2275758]
    
  • Now that we have the get-pip.py file, we can now install pip manually:

      (.venv) theo@DESKTOP-Q1OABO1:/mnt/c/users/dell/desktop/projects/dummy/.venv$ python get-pip.py
      Collecting pip
        Using cached pip-24.3.1-py3-none-any.whl.metadata (3.7 kB)
      Collecting setuptools
        Using cached setuptools-75.6.0-py3-none-any.whl.metadata (6.7 kB)
      Collecting wheel
        Using cached wheel-0.45.1-py3-none-any.whl.metadata (2.3 kB)
      Using cached pip-24.3.1-py3-none-any.whl (1.8 MB)
      Using cached setuptools-75.6.0-py3-none-any.whl (1.2 MB)
      Using cached wheel-0.45.1-py3-none-any.whl (72 kB)
      Installing collected packages: wheel, setuptools, pip
      Successfully installed pip-24.3.1 setuptools-75.6.0 wheel-0.45.1
    

    If you got through that with no problems, congratulations! You have pip installed now. Now let’s proceed with setting up the FastAPI server.

Now that we have that settled, we can finally install FastAPI with pip :

pip install fastapi[standard] psycopg2-binary black isort asyncpg

Let’s get to know our tools:

  • fastapi[standard] - FastAPI package with uvicorn and other necessary packages installed by default.

  • psycopg-binary - PostgreSQL lightweight standalone database adapter that helps ease the connection to the PostgreSQL engine for Python.

  • black - Helps to format code during commits for Python.

  • isort - Formats Python code automatically as per configuration style.

  • asyncpg - Fast asynchronous database library for Python/asyncio and PostgreSQL

Once it’s all installed, next we create a file requirements.txt and we move all our packages into the file:

pip freeze > requirements.txt

Now, that we have our environment ready, we can create a server.

Creating Server

In your project, create a file, main.py or app.py, you can name it whatever you wish. This is the file that initiates your FastAPI app, it can also be the file that loads everything that’s supposed to start up upon initiation. You can also write scripts here, or create endpoints as well. To create a FastAPI instance:

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

origins = [
    "*",
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get('/')
async def main():
    return {
        "status": 200,
        "message": "App initialized"
    }

It’s that easy. If we start the server, you will be able to access the endpoint via: 127.0.0.1:8000:

uvicorn main:app --reload

Our server is live! Now let’s connect to our database.

Connecting to Database

To connect to the database, we need to write a script using the asyncpg library:

import psycopg2
import asyncpg
import os
from dotenv import load_dotenv
from pathlib import Path

async def start_db():
    conn = None
    try:
        conn = await asyncpg.connect(
            host='127.0.0.1',
            port=5432,
            user='your_user',
            password='your_password',
            database='zappy_db' #Or the name of your database created from the previous article
        )
        print("Connecting to PostgreSQL")
        return conn
    except Exception as e:
        print(f"Error: {e}")
    finally:
        if conn:
            print("Connected")

Now we have to call this function in the main file so that when the application starts, our database is connected and active. To do this, create a function with the fastapi.app.on_event decorator.

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from config.database import start_db

app = FastAPI()

origins = [
    "*",
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


@app.on_event("startup")
async def on_startup():
    await start_db()

Now when we reload our server again we get:

uvicorn main:app --reload
INFO:     Will watch for changes in these directories: ['/mnt/c/users/dell/desktop/projects/dummy']
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [25253] using WatchFiles
INFO:     Started server process [25257]
INFO:     Waiting for application startup.
Connecting to PostgreSQL
Connected
INFO:     Application startup complete.

Voila! We have successfully connected our FastAPI server to our PostgreSQL database. Now let’s create a table.

Creating Tables

To create a table, we need to write SQL scripts and run them in the Ubuntu terminal. To do that, create a file and save it with the .sql extension to signify that it is an SQL script. Next, we write our scripts:

-- Create a FILES table 
CREATE TABLE files (
    id           UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id      UUID  NULL,
    file_path    VARCHAR(100) NOT NULL,
    file_name    VARCHAR(50) NOT NULL,
    file_type    file_type_enum,
    file_status  file_status_enum DEFAULT 'pending',
    file_size     BIGINT,
    file_duration INTERVAL,
    created_at   TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updated_at   TIMESTAMP DEFAULT CURRENT_TIMESTAMP NULL
);

Now that we have our scripts for creating the tables files and transcription, we can run a code to create these scripts and create the tables:

psql -U <your_user> -d zappy_db -f create.sql -h localhost

#Confirmation message
CREATE TABLE

And that’s it! We have successfully connected to and created a table in our PostgreSQL database on Ubuntu.

Some useful database operations

  • \dt - Displays all tables/relations in the database.

  • \d <table_name> - Displays table with the table name if it exists on the database.

  • \c <database_name - Connects to the database with the database name.

  • \l - List all databases.

  • \q - Quits/exits from Postgres command-line interface.

  • \conninfo - Shows information about the current database connection.

  • \dn - List all schemas in the database.

  • \di - Lists all indexes in the database.

  • \dv - Lists all views in the database.

Conclusion

This article describes how to successfully connect a PostgreSQL database to a FastAPI server on Ubuntu and how to create a table successfully. I welcome your feedback in the comments section, as I am open to improving both myself and this article.

Thanks for reading!

References