Build an async python service with FastAPI & SQLAlchemy
By: Michael Azimov
Tags: Sqlalchemy, Fastapi, Asynchronous, Python, Asyncio

You can also go to http://127.0.0.1:1111/, and see the “hello_world” response text in our browser.

We have our first async endpoint! 🎆

Book store CRUD app

So, the purpose of this post is to build a small CRUD app. For demonstrations purposes we’ll build a simple book store app, with the ability to create, update and fetch books.

SQLAlchemy and AsyncIO

In order to use a DB for storing the books, I’m using Python’s SQLalchemy library with an sqlite dialect that supports asyncio (aiosqlite)

Install the necessary packages:

pip install SQLAlchemy==1.4.3 aiosqlite

SQLAlchemy 1.4.X is a pretty new release, with lots of upgrades and new features (a first step towards the highly anticipated 2.0 version), that are detailed here. In this post, I will mainly focus on the new async support.

SQLAlchemy Asyncio should be considered alpha level in early 1.4 releases (!).

Once we’ve installed SQLAlchemy, we need to configure our DB connection. Create a new python package named db and a new module inside it, called config.py :

Our DB server is a local instance of SQLLite (a local test.db file) and we will talk to it using the aiosqlite dialect that supports async queries. We are creating the db engine using the new create_async_engine function. The session maker is created with two unique flags: expire_on_commit=False makes sure that our db entities and fields will be available even after a commit was made on the session, and class_=AsyncSession is the new async session. We’ll also use the good, old, declarative_base to configure our soon-to-be-created DB models.

If you want to use a different DB (MySql, PostgreSQL, etc) you need to install a compatible driver with AsyncIO support, and update the DATABASE_URL parameter.

DB Model

We’re building a book store app, so it shouldn’t be a surprise that our main DB table will be, you guessed it — a book. Create a new package inside the db package, called models , and inside it a new module called book.py .

Our book entity will also have some fields — name, author and release year:

In order to create a new entity, we will use a DAL (Data Access Layer) class, that will be responsible for all the sql functions for this DB model. Create a dals package and a book_dal.py module in it:

We have 3 functions: A create_book function that receives our entity fields, and saves a new record to the DB. A get_all_books function that returns all books in the DB, and an update_book function that receives a book_id and optionally new values for the book fields and updates them. In the update_book functions, we are adding an execution option called synchronize_session that tells the session to “refresh” the updated entities using the fetch method, to make sure the entities we have in memory will be up-to-date with the new values we updated.

Now we need some new endpoints that will use these DAL functions, so let’s add them our app.py :

Each of the endpoints asynchronously creates a DB session, a BookDAL instance with the session and calls the relevant function (If your “clean code” instincts are screaming right now, that’s totally fine, we will address them in a few paragraphs).

Pay attention to the PUT method: by using an Optional type with a default value, we make the query parameters as optional.

But, for all of these nice stuff to actually work, we need to create our books table. In order to do that, we will utilize FastAPI’s events feature, and create the necessary tables on app startup (obviously, this is not something we will want to do in a production app):

Now, you can run the app, and check the API Docs page. It should look like this:

The API docs are interactive so you can call our new endpoints right from this page. Create a book: