Building A Simple RAG System With Fastapi (3)
The goal of this series of articles is to build a RAG system with fastapi. In the first article, I designed a fastapi application that gives users the ability to upload documents. These documents are then parsed and stored locally for later usage.
In the second article, I refactored my code to make the application stronger using defensive programming strategies. I also used the open closed principle to make sure my application can be easily extended to support new functionalities.
In this article, I will focus on adding a PostgreSQL database to my application.
But I won’t just add any basic PostgreSQL database. I want my DB to be able to store vector embeddings that I will later on use for similarity search.
Here is a basic representation of our future database schema:

Each file uploaded by the user will be indexed in the “files” table of our database. In the background, we will also add a mechanism to create text chunks from the content of each file and their corresponding embeddings. We will store these chunks in the file_chunks table of our database.
You will need to have PostgreSQL and pgvector installed on your system.
You will also need to install sqlalchemy and psycopg.
pip install sqlalchemy psycopg2 sqlalchemy-utils
Finally, since we want to store text embeddings and be able to query them and manipulate them using cosine similarity, you will need to install pgvector’s python package.
pip install pgvector
To make things a bit industrial, I decided to create a python script to handle the database creation for us. We will only need to add the necessary information in our .env file for the script to work.
Your .env file should at least look like this:
POSTGRES_USERNAME = 'your_username'
POSTGRES_PASSWORD = 'your_password'
Here is the code to create our database. It utilizes SQLAlchemy, a powerful and flexible toolkit for SQL database interaction in Python. The script starts by importing necessary modules from SQLAlchemy for database connection (create_engine
), table and column definitions (Column
, Integer
, String
, Text
, ForeignKey
), and ORM base class creation (declarative_base
). It also imports sessionmaker
for managing database sessions and text
for executing raw SQL commands.
Environment variables are loaded using dotenv
, ensuring sensitive information such as database credentials is not hard-coded into the script. These credentials—username, password, host, port, and database name—are retrieved from environment variables and used to construct the database URL. The password is URL-encoded to handle special characters.
An SQLAlchemy engine
is created with the database URL. This engine is a core component of SQLAlchemy that provides a source of database connectivity and behavior. The script checks if the database exists and, if not, creates it using database_exists
and create_database
utilities.
The database models (tables) are defined using SQLAlchemy ORM’s declarative system, where File
and FileChunk
classes represent tables in the database. The File
class defines a table for storing file information, while FileChunk
includes an additional embedding_vector
column using Vector
from pgvector.sqlalchemy
, indicating advanced vector data handling, likely for machine learning or similar applications.
A database session is created and used to execute the SQL command for enabling the vector
extension, essential for the embedding_vector
column in FileChunk
. Finally, Base.metadata.create_all(engine)
is called within a try-except block to create these tables in the database, with error handling to catch and print any issues that occur during table creation.