Storing data and/or retrieving it from a database is a very common task to perform in a server-side web application.
Bocadillo does not provide its own database layer, and probably never will. However, we do wish to integrate with a recommended solution in a foreseeable future.
In the meantime, you'll need to integrate with third-party libraries. While doing so, you'll definitely find providers handy, as shown in this guide.
Querying a database is typically I/O-bound and can represent a significant part of the request processing time.
This makes asynchronous programming an ideal tool for the job, which is why this page only discusses async solutions.
Databases is a library that "brings async database support to Python". It was built by Tom Christie, the developer behind the Django REST Framework, Starlette, uvicorn, as well as a number of exciting projects in the async Python world.
Here's an example setup that uses providers to create the Database instance and inject it into web views:
# providerconf.py from bocadillo import provider from databases import Database @provider(scope="app") async def db(): async with Database("postgresql://localhost/example") as db: yield db
# app.py from bocadillo import App app = App() @app.route("/") async def index(req, res, db): # TODO: query the `db`! ✨ ...
Using an ORM (Object Relational Mapper) allows you to think in terms of classes and objects instead of tables and rows.
This technique has a number of benefits and drawbacks, but if you go for it the recommended option is orm, an asynchronous ORM for Python. It's based on SQLAlchemy Core, so it's fast, and easy to use, and supports all major databases (PostgreSQL, MySQL and SQLite). Besides, you can integrate it with Bocadillo to get data validation without any boilerplate — see: Use orm to interact with an SQL database.
For completeness, here are some alternatives to
orm we've come across:
- Tortoise: an async ORM inspired by the Django ORM.
- GINO: an async ORM based on SQLAlchemy Core.
- peewee-async: async wrapper around peewee.
Dialect-specific client library (advanced)
If your use case is very specific or low-level, you may want to resort to a dialect-specific async client library.
Here are some of the most popular ones:
Again, you'll definitely want to use providers to setup and teardown database connections. Here's an example for asyncpg:
# providerconf.py import asyncpg from bocadillo import provider @provider(scope="app") async def conn(): conn = await asyncpg.connect( user="user", password="secret", database="example", host="localhost" ) yield conn await conn.close()
If you're using a NoSQL database, many high-quality async clients are already available.
Here are some popular ones:
You can find an example Redis setup at the end of the providers problem statement.