Skip to content

Async support

Everything covered elsewhere in these docs — BaseModel, db.configure(), the query builder — is synchronous: when you call User.get(id=1), your program stops and waits until the database responds before moving on to the next line. For a script or a small CLI tool, that's completely fine.

It becomes a problem inside a web server handling many requests at once (like a FastAPI app). A synchronous database call blocks the entire process while it waits — so one slow query can stall every other request the server is trying to handle at the same time, even ones that have nothing to do with that query. mydborm's async supportAsyncBaseModel and async_db — lets the server hand off to other work while waiting on the database, instead of sitting idle. This assumes you're already familiar with Python's async/await syntax; if not, the short version is that await marks a point where your code can pause and let other tasks run until the thing being awaited is ready.

Installing the async extra (pip install mydborm[async]) pulls in aiomysql and aiopg, the underlying async database drivers mydborm uses for MySQL and YugabyteDB/PostgreSQL respectively.

Configure

The async API mirrors the synchronous one closely, with two differences: every database-touching call needs await in front of it, and configuration happens through async_db instead of db. Define your model by subclassing AsyncBaseModel instead of BaseModel — field definitions work exactly the same way:

import asyncio
from mydborm.async_db import async_db, AsyncBaseModel
from mydborm.fields import IntField, StrField

class AsyncUser(AsyncBaseModel):
    __tablename__ = "users"
    id       = IntField(primary_key=True)
    username = StrField(max_length=100, nullable=False)

async def main():
    await async_db.configure(
        dialect  = "mysql",
        host     = "127.0.0.1",
        port     = 3306,
        user     = "root",
        password = "root",
        database = "mydb",
    )
    await AsyncUser.create_table()
    uid  = await AsyncUser.create(username="alice")
    user = await AsyncUser.get(id=uid)
    all  = await AsyncUser.all()
    await async_db.close()

asyncio.run(main())

A few things to notice if you're coming from the sync API:

  • async_db.configure(...) must itself be awaited — it opens a connection pool in the background, which is itself an async operation.
  • create_table(), create(), get(), all(), filter(), update(), and delete() all exist on AsyncBaseModel with the same names and arguments as BaseModel — just await each call.
  • Call await async_db.close() when you're done (for example, when your application shuts down) to close the pool's connections cleanly.

In a real FastAPI app, you'd typically call async_db.configure(...) once during application startup, and then await model calls inside your endpoint functions — that's what lets FastAPI keep serving other requests while one endpoint is waiting on a slow query.

YugabyteDB async

Async support works the same way against YugabyteDB (or PostgreSQL) — just change the dialect, port, and credentials to match:

await async_db.configure(
    dialect  = "yugabyte",
    host     = "127.0.0.1",
    port     = 5433,
    user     = "yugabyte",
    password = "yugabyte",
    database = "yugabyte",
)

Everything else — AsyncUser.create(), .get(), .all(), and so on — works identically regardless of which dialect you configured.