DEV Community

Leandro Damascena
Leandro Damascena

Posted on

pydynox: A fast DynamoDB ORM for Python with a Rust core.

I've been building serverless apps for years. Lambda, DynamoDB, API Gateway - the usual stack. It's fast, it scales, and once you understand the access patterns, it's a great database. But I always felt like the Python libraries could be better.

The problem

If you use Python with DynamoDB, you have a few options:

  1. boto3 - Works great, but you write a lot of code for simple things
  2. PynamoDB - Nice ORM, though it can be slow with big datasets
  3. Other libraries - Each has its own trade-offs

I wanted something that felt like a modern ORM but didn't sacrifice speed. Especially for Lambda functions where cold starts and execution time matter.

Learning Rust

About a year ago, I started learning Rust. It's a promising language - fast, safe, and growing fast. The syntax and concepts (ownership, borrowing, lifetimes) were hard at first. But once it clicks, it makes sense.

I already knew about PyO3 and always liked how it creates Python bindings. It handles the hard stuff - OS-level optimizations, serialization between Rust and Python, memory management. That's when I thought: why not use this for DynamoDB?

What is pydynox?

pydynox is a DynamoDB library for Python. It has a Rust core that handles the heavy work - serialization, compression, encryption - while keeping a friendly Python API on top.

I wanted something that feels nice to use, runs fast, and works well in environments like Lambda where every millisecond counts.

Getting started

Install with pip:

pip install pydynox
Enter fullscreen mode Exit fullscreen mode

Define a model:

from pydynox import Model, ModelConfig, StringAttribute, NumberAttribute

class User(Model):
    model_config = ModelConfig(table="users")

    pk = StringAttribute(hash_key=True)
    sk = StringAttribute(range_key=True)
    name = StringAttribute()
    age = NumberAttribute(default=0)
Enter fullscreen mode Exit fullscreen mode

Use it:

# Create and save
user = User(pk="USER#123", sk="PROFILE", name="Alice", age=30)
user.save()

# Get by key
user = User.get(pk="USER#123", sk="PROFILE")

# Update
user.update(age=31)

# Delete
user.delete()
Enter fullscreen mode Exit fullscreen mode

If you use Pydantic, there's an integration for that too:

from pydynox.integrations.pydantic import dynamodb_model

@dynamodb_model(table="users", hash_key="pk", range_key="sk")
class User(BaseModel):
    pk: str
    sk: str
    name: str
    age: int = 0
Enter fullscreen mode Exit fullscreen mode

What runs in Rust?

The parts that matter for speed:

  • Serialization - converting Python dicts to DynamoDB format and back
  • Compression - zstd, lz4, gzip for large attributes
  • Encryption - AES-GCM with KMS for sensitive fields
  • Rate limiting - token bucket algorithm
  • Batch operations - chunking, retries, error handling

These run on every request. Rust makes them fast.

Other things you can do

Beyond basic CRUD, pydynox supports the DynamoDB features you'd expect.

You can query with conditions and filters. Batch writes work without worrying about the 25-item limit - pydynox splits and retries for you. Same for batch gets with the 100-item limit.

Transactions are there when you need atomic operations across items. Async support if you're using asyncio. Lifecycle hooks let you run code before save, after get, or on delete - good for validation or logging.

Global Secondary Indexes work too. Define them in your model and query by non-key attributes.

There's also built-in observability. Every operation returns metrics - duration, consumed RCU/WCU, item count. You can enable logging to see what's happening. When something is slow or costs more than expected, you'll know. I have plans to add OpenTelemetry and Prometheus exporters too.

I have a big backlog of ideas I want to add - things like query caching, schema migrations, and better debugging tools. But first I want to make sure the core is solid.

Current status

pydynox is in alpha. I use it in personal projects and it works well, but before going to beta I want more people to try it.

If you work with DynamoDB, I'd love for you to try it out. Test it, break it, tell me what's confusing. Open an issue if something doesn't work. Start a discussion if you have ideas.

Contributions are welcome too - whether it's code, docs, or just feedback. The repo has a backlog of features if you want to pick something up.

Links

Top comments (0)