Async support
pydynox supports async/await for high-concurrency applications like FastAPI, aiohttp, and other asyncio-based frameworks.
Why async?
Sync operations block the event loop:
async def handle_request(user_id: str):
user = User.get(pk=user_id, sk="PROFILE") # Blocks!
# Other async tasks can't run while waiting for DynamoDB
Async operations let other tasks run while waiting for I/O:
async def handle_request(user_id: str):
user = await User.async_get(pk=user_id, sk="PROFILE") # Non-blocking
# Other tasks can run while waiting
Model async methods
All Model CRUD operations have async versions with async_ prefix:
from pydynox import Model, ModelConfig
from pydynox.attributes import NumberAttribute, StringAttribute
class User(Model):
model_config = ModelConfig(table="users")
pk = StringAttribute(hash_key=True)
sk = StringAttribute(range_key=True)
name = StringAttribute()
age = NumberAttribute()
async def main():
# Create and save
user = User(pk="USER#123", sk="PROFILE", name="John", age=30)
await user.async_save()
# Get by key
user = await User.async_get(pk="USER#123", sk="PROFILE")
# Update
await user.async_update(name="Jane", age=31)
# Delete
await user.async_delete()
Client async methods
The DynamoDBClient also has async versions:
from pydynox import DynamoDBClient
async def main():
client = DynamoDBClient()
# Get item
await client.async_get_item("users", {"pk": "USER#123", "sk": "PROFILE"})
# Put item
await client.async_put_item("users", {"pk": "USER#123", "name": "John"})
# Update item
await client.async_update_item(
"users",
{"pk": "USER#123", "sk": "PROFILE"},
updates={"name": "Jane"},
)
# Delete item
await client.async_delete_item("users", {"pk": "USER#123", "sk": "PROFILE"})
Async query
Query returns an async iterator:
from pydynox import DynamoDBClient
client = DynamoDBClient()
async def iterate_results():
# Iterate with async for
async for item in client.async_query(
"users",
key_condition_expression="#pk = :pk",
expression_attribute_names={"#pk": "pk"},
expression_attribute_values={":pk": "USER#123"},
):
print(item["name"])
async def collect_results():
# Collect all results
items = await client.async_query(
"users",
key_condition_expression="#pk = :pk",
expression_attribute_names={"#pk": "pk"},
expression_attribute_values={":pk": "USER#123"},
).to_list()
return items
Concurrent operations
The real power of async is running operations concurrently:
import asyncio
from pydynox import Model, ModelConfig
from pydynox.attributes import NumberAttribute, StringAttribute
class User(Model):
model_config = ModelConfig(table="users")
pk = StringAttribute(hash_key=True)
sk = StringAttribute(range_key=True)
name = StringAttribute()
age = NumberAttribute()
async def sequential():
# Sequential - slow (100ms + 100ms + 100ms = 300ms)
user1 = await User.async_get(pk="USER#1", sk="PROFILE")
user2 = await User.async_get(pk="USER#2", sk="PROFILE")
user3 = await User.async_get(pk="USER#3", sk="PROFILE")
return user1, user2, user3
async def concurrent():
# Concurrent - fast (~100ms total)
user1, user2, user3 = await asyncio.gather(
User.async_get(pk="USER#1", sk="PROFILE"),
User.async_get(pk="USER#2", sk="PROFILE"),
User.async_get(pk="USER#3", sk="PROFILE"),
)
return user1, user2, user3
Real world example
Fetch user and their orders at the same time:
import asyncio
from pydynox import DynamoDBClient, Model, ModelConfig
from pydynox.attributes import StringAttribute
client = DynamoDBClient()
class User(Model):
model_config = ModelConfig(table="users")
pk = StringAttribute(hash_key=True)
sk = StringAttribute(range_key=True)
name = StringAttribute()
async def get_user_with_orders(user_id: str):
"""Fetch user and their orders at the same time."""
user, orders = await asyncio.gather(
User.async_get(pk=f"USER#{user_id}", sk="PROFILE"),
client.async_query(
"orders",
key_condition_expression="#pk = :pk",
expression_attribute_names={"#pk": "pk"},
expression_attribute_values={":pk": f"USER#{user_id}"},
).to_list(),
)
return {"user": user, "orders": orders}
FastAPI example
from fastapi import FastAPI
from pydynox import DynamoDBClient, Model, ModelConfig, set_default_client
from pydynox.attributes import StringAttribute
app = FastAPI()
client = DynamoDBClient()
set_default_client(client)
class User(Model):
model_config = ModelConfig(table="users")
pk = StringAttribute(hash_key=True)
sk = StringAttribute(range_key=True)
name = StringAttribute()
@app.get("/users/{user_id}")
async def get_user(user_id: str):
user = await User.async_get(pk=f"USER#{user_id}", sk="PROFILE")
if not user:
return {"error": "User not found"}
return {"name": user.name}
@app.post("/users/{user_id}")
async def create_user(user_id: str, name: str):
user = User(pk=f"USER#{user_id}", sk="PROFILE", name=name)
await user.async_save()
return {"status": "created"}
Available async methods
Model
| Sync | Async |
|---|---|
Model.get() |
Model.async_get() |
model.save() |
model.async_save() |
model.delete() |
model.async_delete() |
model.update() |
model.async_update() |
DynamoDBClient
| Sync | Async |
|---|---|
get_item() |
async_get_item() |
put_item() |
async_put_item() |
delete_item() |
async_delete_item() |
update_item() |
async_update_item() |
query() |
async_query() |
Notes
- Async methods use the same Rust core as sync methods
- No extra dependencies needed
- Works with any asyncio event loop
- Hooks still run synchronously (before/after save, etc.)