Connecting Fast API to PostgreSQL Database

When it comes to working with a PostgreSQL database within a Python application, we will need a PostgreSQL driver, there are a handful of libraries that we can use to achieve this and more on that shortly, but first let us answer the question, What is a PostgreSQL driver?

What is a PostgreSQL Driver:

In the context of PostgreSQL and Python, a driver is a software module or library that allows Python programs to connect and interact with a PostgreSQL database. A PostgreSQL driver provides an interface between the Python programming language and the PostgreSQL database, allowing developers to perform database operations such as querying, inserting, updating, and deleting data.

The driver typically implements the standard Python Database API (Python DB-API) specification, which defines a set of methods, parameters, and exceptions for communicating with a database. This specification provides a common interface that can be used by different Python database modules, allowing developers to write portable code that can work with multiple databases.

PostgreSQL drivers for Python, such as psycopg2, asyncpg, and aiopg are specifically designed to work with PostgreSQL databases and provides a set of functions and methods for connecting to a PostgreSQL server, executing SQL statements, and managing database transactions. These drivers typically support both sync and async interfaces, allowing developers to choose the appropriate interface for their application's needs.

Libraries that Facilitate the Connection of PostgreSQL Database to FastAPI

Here are Libraries used to connect a PostgreSQL database to FastAPI:

  • asyncpg: asyncpg is a fast, efficient, and easy-to-use PostgreSQL database driver that is designed specifically for use with asyncio. It provides a simple and high-level interface for asynchronously working with PostgreSQL databases.

  • aiopg: aiopg is another asyncio-based PostgreSQL database driver that provides a high-level interface for asynchronously working with PostgreSQL databases. It supports both Python 3.5+ and PostgreSQL 9.1+.

  • sqlalchemy: sqlalchemy is a SQL toolkit and ORM that provides a set of high-level APIs for working with relational databases, including PostgreSQL. It supports both sync and async interfaces and provides an ORM for mapping database tables to Python objects.

  • psycopg2: is a popular PostgreSQL database adapter for the Python programming language. It provides a comprehensive implementation of the Python DB-API specification for interacting with PostgreSQL databases. psycopg2 is widely used in the Python community for building web applications, data analysis tools, and other data-intensive applications that require access to a PostgreSQL database.

All of these libraries enumerated above are compatible with FastAPI and can be used to connect to a PostgreSQL database in your FastAPI application.

What we will be using and why

In this quick tutorial we will be using psycopg2 library to connect our FastAPI to our database and here is why:

psycopg2 is a fast and efficient driver that provides a high level of performance and reliability. It supports a wide range of PostgreSQL features, including transaction management, stored procedures, and user-defined types. It also provides a set of advanced features, such as asynchronous support, server-side cursors, and connection pooling.

One of the benefits of using psycopg2 is its popularity and extensive documentation. There are many resources available online that guide how to use psycopg2, including official documentation, third-party tutorials, and code examples. This makes it easier for developers to get started with psycopg2 and troubleshoot any issues that they may encounter.

Prerequisites

Before we begin, I would want to assume that you have the following at your disposal

  • Any Code interpreter

  • Postgres installed on your machine

  • A created PostgreSQL database in PgAdmin although it comes with a default database created named ‘Postgres’. You can create yours if you want

  • The details used in the creation of the database (very important).

With all of the above in place, ready to get your hands dirty?

Making the Connection

To connect FastAPI to a PostgreSQL database, follow these steps:

  1. Install the required packages: use pip install psycopg2-binary fastapi[all] to install the required library. We add the [all] to install all the sub packages, we will need. Saves you the hassle of installing them individually.

     pip install psycopg2-binary fastapi[all]
    
  2. Import the required packages: we then import both the driver and fast API

     from fastapi import FastAPI
     import psycopg2
     from psycopg2.extras import RealDictCursor
    
  3. Create a connection to the PostgreSQL database: Fill in the required information. The database name, password and user-required fields are all the details you used in setting up your PostgreSQL server and database in PgAdmin.

     conn = psycopg2.connect(
         database="your_database_name",
         user="your_username",
         password="your_password",
         host="localhost",
         port="5432",
         cursor_factory=RealDictCursor)
    
  4. Create a cursor object to interact with the database: This is calling the cursor method and having it in a variable named 'cur'. It is used to execute SQL statements.

     cur = conn.cursor()
    
  5. Define your FastAPI app: What this means is that, we are setting up a variable called app, that will represent or be equal to FastAPI(). Anytime we want to access FastAPI(), we just use 'app'. hopefully, you get the explanation.

     app = FastAPI()
    
  6. Define your database endpoint and query: Much need not be said here, if you are working with FastAPI, I suppose you understand exactly what is happening here. If not then you can check out some tutorials to help.

     @app.get("/users")
     async def read_users():
         cur.execute("SELECT * FROM users")
         users = cur.fetchall()
         return {"users": users}
    
  7. Close the cursor and connection objects when done

     cur.close()
     conn.close()
    

You should have something that looks like this

from fastapi import FastAPI
import psycopg2
from psycopg2.extras import RealDictCursor

app = FastAPI()

    # Connect to an existing database
    conn = psycopg2.connect(host='localhost', 
    dbname='fastapi', 
    user='postgres',
    password='xxxxxxxxxx.',
    cursor_factory=RealDictCursor)

    # Open a cursor to perform database operations
    cursor = conn.cursor()

#creating an endpoint/path

@app.get("/users")
async def read_users():
    cur.execute("SELECT * FROM users")
    users = cur.fetchall()
    return {"users": users}

cur.close()
conn.close()

That's it! You have successfully connected to your PostgreSQL database using psycopg2.

Best Practice

When it comes to connecting to a database the connection might fail, and so, anytime we want to make a connection to a database that has a potential of failing, it is advisable to use Exception Handling.

This is not mandatory, it is not a must that you do this but If you follow through, it will be extremely helpful. one of the many things this procedure does is that it helps you know if you successfully connected to the database and if not generates an error message stating why the connection failed.

A few things can cause the connection failure like unstable network conditions, wrong syntax and a few more. With all these in mind, let's jump right in!

  1. First, we try the connection procedure using the try exception, if the connection is successful, it prints 'database connected'.

     from fastapi import FastAPI
     import psycopg2
     from psycopg2.extras import RealDictCursor
    
     app = FastAPI()
    
     try:
         # Connect to an existing database
         conn = psycopg2.connect(host='localhost', 
         dbname='fastapi', 
         user='postgres',
         password='xxxxxxxxxxxxx.',
         cursor_factory= RealDictCursor)
    
         # Open a cursor to perform database operations
         cursor = conn.cursor()
         print('database connected')
    
  2. Secondly, if the connection is unsuccessful, we try to catch the error using except. Make sure to indent properly.

       except Exception as error:
         print('connecting to database unsucessful')
         print('Error: ', error)
    
  3. We are almost done, next, we want this exception handling to run for as many times as necessary until the database finally connects successfully.

    To do this, we use a while loop and break out of the loop if the connection is successful using the break keyword.

     from fastapi import FastAPI
     import psycopg2
     from psycopg2.extras import RealDictCursor
    
     app = FastAPI()
    
     while True:
         try:
             # Connect to an existing database
             conn = psycopg2.connect(host='localhost', 
             dbname='fastapi', 
             user='postgres',
             password='xxxxxxxxxxxxx.',
             cursor_factory= RealDictCursor)
    
             # Open a cursor to perform database operations
             cursor = conn.cursor()
             print('database connected')
             break 
    
             except Exception as error:
                 print('connecting to database unsucessful')
                 print('Error: ', error)
    
  4. Finally, it will be a good idea to wait a few seconds before trying to reconnect to the database if the connection failed, maybe due to network issues or something else.

    To do this, we import the time module and place this beneath the except keyword, time.sleep(2). After every two seconds, the Fast API attempts a connection to the database. You can put any time duration you want.

     from fastapi import FastAPI
     import psycopg2
     from psycopg2.extras import RealDictCursor
    
     app = FastAPI()
    
     while True:
         try:
             # Connect to an existing database
             conn = psycopg2.connect(host='localhost', 
             dbname='fastapi', 
             user='postgres',
             password='xxxxxxxxxxxxx.',
             cursor_factory= RealDictCursor)
    
             # Open a cursor to perform database operations
             cursor = conn.cursor()
             print('database connected')
             break 
    
             except Exception as error:
                 print('connecting to database unsucessful')
                 print('Error: ', error)
                 time.sleep(2)
    

And with that, everything should work smoothly.

I hope you understood everything we did, if you don't feel free to reach out to me, drop your questions in the comment section and I will be more than happy to attend to them.

Cheers!