Python SDK
The taruvi package is the Python client for TaruviBase. Use it in backends,
scheduled jobs, scripts, notebooks, and TaruviBase functions. It has a
synchronous client and an asynchronous client with the same modules:
client.database, client.storage, client.functions, and more.
This guide covers taruvi 0.2.3. It takes you from install to a first query,
then explains responses, errors, timeouts, and retries.
Before you begin#
You need:
- Python 3.10 or newer.
- A TaruviBase app, with
TARUVI_SITE_URLandTARUVI_APP_SLUGfrom Settings → Connect in TaruviBase Console. - An API key for server code. On the same page, select Generate API Key. The key is shown once; see Issue API tokens.
Install the SDK#
python -m pip install taruvi
Pin the version in your requirements file. taruvi.__version__ reports the
installed version.
Create a client#
Keep credentials in environment variables or a secret store, never in source:
export TARUVI_SITE_URL="https://YOUR_SITE_HOST"
export TARUVI_APP_SLUG="APP_SLUG"
printf 'TaruviBase API key: ' && read -rs TARUVI_API_KEY && export TARUVI_API_KEY && printf '\n'
- Synchronous
- Asynchronous
import os
from taruvi import Client
client = Client(
api_url=os.environ["TARUVI_SITE_URL"],
app_slug=os.environ["TARUVI_APP_SLUG"],
api_key=os.environ["TARUVI_API_KEY"],
mode="sync",
)
The sync client uses a blocking httpx.Client. It works in scripts, Django
views, Celery tasks, and notebooks. Call client.close() when you're done, or
use it as a context manager: with Client(...) as client:.
import asyncio
import os
from taruvi import Client
async def main() -> None:
async with Client(
api_url=os.environ["TARUVI_SITE_URL"],
app_slug=os.environ["TARUVI_APP_SLUG"],
api_key=os.environ["TARUVI_API_KEY"],
mode="async",
) as client:
result = await client.database.from_("tasks").page_size(20).execute()
print(result["total"])
asyncio.run(main())
The async client uses httpx.AsyncClient. Await every call, including
client.close() when you don't use async with.
Client() takes api_url and app_slug, then keyword options:
mode—"sync"or"async". Without it, the client is async inside a running event loop and sync otherwise. Set it explicitly in shared code.api_key,jwt,session_token— The credential to send. Defaults to none; see Authenticate with the Python SDK.timeout— Seconds to wait for each request: a whole number from 1 to 300. Defaults to120. Other values raise pydantic'sValidationError.max_retries— Retries for a request that fails to connect or times out. Defaults to3. See Timeouts and retries.
When you pass no credential, the client reads TARUVI_API_KEY, TARUVI_JWT,
or TARUVI_SESSION_TOKEN from the environment or from a .env file in the
working directory. A client you meant to leave unauthenticated then acts as
that credential's user. A credential you pass always replaces the environment's.
Check client.is_authenticated when it matters.
Make your first request#
This query reads the first 20 open tasks, sorted by title. The
quickstart creates the tasks table.
from taruvi_client import client
result = (
client.database.from_("tasks")
.filter("done", "eq", False)
.sort("title", "asc")
.page_size(20)
.execute()
)
for task in result["data"]:
print(task["id"], task["title"])
print("Matching tasks:", result["total"])
execute() sends
GET /api/apps/APP_SLUG/datatables/tasks/data/?ordering=title&page_size=20&done=false.
How requests work#
from_() starts a query. The builder is mutable: each method changes it
and returns it for chaining. Start each request with a fresh from_() call
rather than reusing a builder.
Operations are staged, then sent. get(id), create(), update(),
upsert(), delete(), bulk_delete(), and delete_filtered() choose the
operation. execute() sends it:
created = client.database.from_("tasks").create({"title": "Ship docs", "done": False}).execute()
task_id = created["data"][0]["id"]
client.database.from_("tasks").get(task_id).update({"done": True}).execute()
Modules share the client. client.database, client.storage,
client.functions, client.secrets, client.policy, client.users,
client.app, client.settings, and client.analytics all use the client's
address and credential. The method reference
lists them.
Read the response#
Methods return plain dictionaries and lists.
| Call | Returns |
|---|---|
List read: execute() without an operation | {"data": [...], "total": N} |
get(id).execute() | {"data": {...}, "total": None}; data is the record |
create(), update(), or upsert(), then execute() | The full response envelope: status, message, and data |
first() | The first record, or None. It requests a single row |
count() | An integer: the number of matching rows. It requests a single row |
create() returns data as a list, even for one record. delete(id) returns
an empty dict, and bulk_delete() and delete_filtered() return
{"deleted_count": N, "message": ...}. When nothing matches,
delete_filtered() returns the envelope instead, with the count in
result["data"]["deleted_count"].
Set a page size. Without page_size(), a list read returns every matching
row, and page() has no effect. The largest page size is 1,000 by default; a
larger page_size() raises ValidationError rather than being reduced for
you. To read everything in pages:
page = 1
while True:
result = client.database.from_("tasks").page(page).page_size(500).execute()
for task in result["data"]:
print(task["id"])
if page * 500 >= result["total"]:
break
page += 1
Handle errors#
API failures raise a TaruviError subclass. Each carries status_code, the
platform's code and detail, and field-level details:
from taruvi import (
AuthenticationError,
AuthorizationError,
BillingError,
ConnectionError,
TaruviError,
TimeoutError,
ValidationError,
)
from taruvi_client import client
try:
client.database.from_("tasks").create({"title": ""}).execute()
except ValidationError as error:
print("Fix these fields:", error.details)
except AuthenticationError:
# Also catches NotAuthenticatedError, raised when the client has no credential.
print("The credential is missing, invalid, or expired")
except AuthorizationError as error:
print("Authenticated, but not allowed:", error.detail)
except BillingError as error:
print("Blocked by the organization's billing:", error.message)
except (TimeoutError, ConnectionError) as error:
print("TaruviBase could not be reached:", error)
except TaruviError as error:
print(error.status_code, error.code, error.message)
TimeoutError and ConnectionError are the SDK's classes, imported from
taruvi. They shadow Python's built-ins of the same name in that module. The
error reference lists every class.
Timeouts and retries#
- Timeouts. Each request waits up to
timeoutseconds. A single function call can wait longer withclient.functions.execute(..., timeout=300). - Retries. Up to
max_retriesmore attempts, after 1, 2, and 4 seconds.GET,PUT, andDELETEare retried after a timeout or a dropped connection.POSTandPATCHare retried only when the connection never opened, so a create or a function run is never sent twice. - HTTP errors are not retried. A
4xxor5xxresponse raises at once. - Uploads and downloads aren't retried; failures still raise the SDK's
TimeoutErrororConnectionError.