Getting Started
Installing Beanis
You can simply install Beanis from PyPI:
PIP
pip install beanis
Poetry
poetry add beanis
Initialization
Getting Beanis setup in your code is really easy:
- Write your database model as a Pydantic class but use
beanis.Documentinstead ofpydantic.BaseModel - Initialize Redis async client
- Call
beanis.init_beaniswith the Redis client and list of Beanis models
The code below should get you started and shows some of the field types that you can use with Beanis.
import asyncio
from typing import Optional
from redis.asyncio import Redis
from pydantic import BaseModel
from beanis import Document, Indexed, init_beanis
class Category(BaseModel):
name: str
description: str
# This is the model that will be saved to Redis
class Product(Document):
name: str # You can use normal types just like in pydantic
description: Optional[str] = None
price: Indexed(float) # Indexed for range queries
category: Category # You can include pydantic models as well
stock: int = 0
class Settings:
name = "products" # Redis key prefix
async def main():
# Initialize Redis client
# IMPORTANT: decode_responses=True is required!
client = Redis(
host="localhost",
port=6379,
db=0,
decode_responses=True
)
# Initialize Beanis with the Product document
await init_beanis(database=client, document_models=[Product])
# Now you can use your models!
chocolate = Category(
name="Chocolate",
description="A preparation of roasted and ground cacao seeds."
)
product = Product(
name="Tony's Chocolonely",
price=5.95,
category=chocolate,
stock=100
)
# Insert the document
await product.insert()
print(f"Inserted product with ID: {product.id}")
# Retrieve by ID
found = await Product.get(product.id)
print(f"Found: {found.name} - ${found.price}")
# Query by price
affordable = await Product.find(Product.price < 10.0).to_list()
print(f"Affordable products: {len(affordable)}")
# Update
await product.update(price=6.95, stock=150)
print(f"Updated price to ${product.price}")
# Get all products
all_products = await Product.all()
print(f"Total products: {len(all_products)}")
# Delete
await product.delete_self()
print("Product deleted")
# Clean up
await client.close()
if __name__ == "__main__":
asyncio.run(main())
What's Next?
Now that you have Beanis set up, you can explore:
- Defining Documents - Learn about document models
- Insert Operations - How to create documents
- Find Operations - How to query documents
- Update Operations - How to modify documents
- Custom Encoders - Store NumPy arrays, custom types
Key Differences from Beanie (MongoDB)
If you're coming from Beanie, here are the key differences:
- Redis client instead of Motor: Use
redis.asyncio.Redisinstead ofmotor.AsyncIOMotorClient - decode_responses=True required: Redis needs this to return strings instead of bytes
- Hash storage: Documents are stored as Redis Hashes, not MongoDB documents
- Indexed fields: Uses Redis Sorted Sets/Sets for indexes, not MongoDB indexes
- No aggregation pipelines: Use Redis native operations or query methods
- Custom encoders: Built-in system for storing complex types like NumPy arrays
Common Patterns
Insert with TTL
# Document expires after 1 hour
await product.insert(ttl=3600)
Batch Operations
# Insert many documents efficiently
products = [
Product(name=f"Product {i}", price=float(i * 10))
for i in range(100)
]
await Product.insert_many(products)
# Get many documents
ids = [p.id for p in products]
found = await Product.get_many(ids)
Event Hooks
from beanis import before_event, after_event, Insert, Update
class Product(Document):
name: str
price: float
@before_event(Insert)
async def validate_price(self):
if self.price < 0:
raise ValueError("Price cannot be negative")
@after_event(Insert)
async def log_creation(self):
print(f"Created product: {self.name}")
Troubleshooting
"All keys must be strings" error
Make sure you set decode_responses=True when creating the Redis client:
client = Redis(decode_responses=True) # ✅ Correct
client = Redis() # ❌ Will cause errors
Type validation errors
Beanis validates data using Pydantic. If you get validation errors, check that your data matches the model:
# ❌ This will fail - price must be float
product = Product(name="Test", price="not a number")
# ✅ This works
product = Product(name="Test", price=9.99)
Performance tips
- Use batch operations (
insert_many,get_many) for multiple documents - Skip validation on reads for better performance (default behavior)
- Use TTL to automatically expire old data
- Use indexed fields for range queries
Next Steps
- Explore the tutorial for in-depth guides
- Check out custom encoders for storing complex types
- Read the README for feature comparisons