Use Database with async Python
Use the asynchronous client in ASGI applications, workers, and services that already run an event loop. It exposes the same Database query builder as the synchronous client; only operations that make an HTTP request are awaited.
Configure one client
Install the taruvi package, then create the client with
mode="async". An async context manager closes the underlying HTTP connection
pool when the application scope ends.
import os
from taruvi import Client
def create_taruvi_client():
return Client(
api_url=os.environ["TARUVI_SITE_URL"],
app_slug=os.environ["TARUVI_APP_SLUG"],
api_key=os.environ["TARUVI_API_KEY"],
mode="async",
)
For a long-running application, create one client during startup and call
await client.close() during shutdown. For a bounded job, use it directly as
an async context manager:
async with create_taruvi_client() as client:
await (
client.database
.from_("tasks")
.filter("done", "eq", False)
.sort("title", "asc")
.sort("id", "asc")
.page_size(20)
.execute()
)
Know what to await
Builder methods only describe the request and remain synchronous. Await the terminal method or a direct Database operation.
| Compose synchronously | Await the request |
|---|---|
from_(), filter(), sort(), page(), page_size() | execute(), first(), count() |
allowed_actions(), aggregate(), group_by(), having() | database.get(), database.create(), database.update(), database.delete() |
search(), vector_search(), hybrid() | client.close() |
Apply the async form to task guides
The asynchronous client keeps the same method names, arguments, and result
shapes as the synchronous Python client. Add await only where the table
above identifies an HTTP operation:
task = await client.database.get("tasks", task_id)
tasks = await (
client.database
.from_("tasks")
.filter("done", "eq", False)
.sort("title", "asc")
.sort("id", "asc")
.page(1)
.page_size(20)
.execute()
)
Use the canonical task guides for the complete behavior and result contract:
- Create, read, update, and delete records
- Run bounded bulk operations
- Compose filters, ordering, and pagination
- Run aggregate queries
- Search records
The table must already have the schema and indexes required by the selected
operation. To include related records, use populate; see
Relationships.
Run record writes asynchronously
Await each direct record method. task_id is the UUID returned by create or
one supplied by the application;
records, updates, and task_ids are bounded lists assembled by the
application.
- Python SDK
await client.database.create(
"tasks",
{"id": task_id, "title": "Verify async writes", "done": False},
)
await client.database.get("tasks", task_id)
await client.database.update("tasks", task_id, {"done": True})
await client.database.delete("tasks", task_id)
await client.database.create("tasks", records)
await client.database.update("tasks", updates)
await (
client.database
.from_("tasks")
.bulk_delete(task_ids)
.execute()
)
Direct create() returns the created record list. Direct get() and a
single-record update() return the record object. A successful direct delete
returns None; ID-based bulk delete returns the service's deletion summary.
Run independent reads concurrently
Create a separate builder for each request. The client can reuse its HTTP
connection pool while asyncio.gather waits for independent reads. Import
Python's asyncio module once in your application setup.
- Python SDK
await asyncio.gather(
client.database.from_("tasks").filter("done", "eq", False).count(),
client.database.from_("tasks").sort("title", "asc").sort("id", "asc").first(),
)
Do not share or mutate one query-builder instance across concurrent tasks. Each builder stores its filters, ordering, pagination, and configured operation until execution.
Configure retries deliberately
The asynchronous HTTP client retries connection and timeout failures with
exponential backoff. max_retries defaults to 3, which permits up to four
total attempts, and applies to reads and writes. HTTP responses such as 429
and 5xx are not retried; the client raises the corresponding SDK API
exception. The setting is client-wide; Database operations do not expose a
per-request override. Set it explicitly for the workload, and avoid automatic
retries for a write unless the operation has an application-level idempotency
strategy.
client = Client(
api_url=os.environ["TARUVI_SITE_URL"],
app_slug=os.environ["TARUVI_APP_SLUG"],
api_key=os.environ["TARUVI_API_KEY"],
mode="async",
max_retries=0,
)
Continue with record operations, query composition, or the Database interface reference.