Skip to main content

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 synchronouslyAwait 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:

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.

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.

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.