TinyStore¶
A lightweight, Pydantic-native relational database that persists data as human-readable JSON files. No external server, no daemon, no binary format — just validated models on disk.
TinyStore combines TinyDB-like simplicity with SQLite/ORM relational concepts: typed Pydantic v2 models, a Pythonic query DSL, unique and foreign-key constraints, in-memory joins, write-ahead-journaled transactions, and file locking. It is built for small apps, CLI tools, prototypes, tests, and embedded datasets — not for high-volume or high-concurrency workloads.
Why TinyStore?¶
- JSON on disk. Every table is a separate JSON file you can read, diff, and back up with any tool.
- Validated by Pydantic v2. Data is coerced and validated on the way in
and out —
ge=,min_length=, custom types, the works. - Typed query DSL. Field comparisons are made against the model
class (
User.age < 35) — no magic strings, noeval. - Relational. Foreign keys, unique constraints, indexes, relationships, and in-memory joins.
- Atomic transactions. A write-ahead journal makes multi-table transactions all-or-nothing across files.
- Cross-process safe. OS-level file locking via
portalocker.
Install¶
Or with uv:
Quick example¶
from tinystore import Database, Field, Model
class User(Model):
id: int | None = Field(default=None, primary_key=True)
name: str
email: str = Field(unique=True)
age: int | None = Field(default=None, index=True)
# A database is just a directory on disk (created if missing).
db = Database("./app_data")
db.register(User)
alice = db.insert(User(name="Alice", email="alice@example.com", age=30))
young = (
db.select(User)
.where(User.age < 35)
.order_by(User.name)
.all()
)
Where next?¶
- New to TinyStore? Start with the Getting started.
- Learn by feature in the Guides.
- Browse every class, method, and exception in the API reference.
- See it all together in the Examples — a blog app, a task queue, a library catalog, and a contacts CLI.