Skip to main content

Your First BinaryDB Application

This guide walks you through creating, loading, manipulating, and persisting data with BinaryDB. By the end, you’ll understand the complete lifecycle of working with a BinaryDB database.
1

Import the Database class

Start by importing the Database class from the binarydb package:
from binarydb.database import Database
This is the main class you’ll interact with. It provides all the methods for storing, retrieving, and managing your data.
2

Create a database instance

Create a database instance by specifying a file path:
db = Database("./data/mydb")
This creates a database that will be stored at ./data/mydb.pkl. The .pkl extension is added automatically.
The database file won’t be created until you call commit(). If the directory doesn’t exist, you’ll need to create it first:
import os
os.makedirs("./data", exist_ok=True)
3

Load existing data (if any)

If a database file already exists at the path, load it:
db.load()
This reads the database from disk and loads all records into memory. If the file doesn’t exist, this method does nothing—your database will simply start empty.
Always call load() before working with a database that might already contain data. Otherwise, you’ll start with an empty database and overwrite existing data when you commit.
4

Store data with set()

Add records to your database using the set() method:
# Store a dictionary
db.set("user:1", {"name": "Ana", "age": 30})

# Store different data types
db.set("config", {"theme": "dark", "language": "en"})
db.set("counter", 42)
db.set("tags", ["python", "database", "tutorial"])
You can store any pickle-serializable Python object: dictionaries, lists, numbers, strings, custom objects, etc.Expected output:
(No output - data is stored in memory)
5

Retrieve data with get()

Fetch records by their key:
user = db.get("user:1")
print(user)
# Output: {'name': 'Ana', 'age': 30}

# Provide a default value if the key doesn't exist
theme = db.get("theme", default="light")
print(theme)
# Output: light
The get() method works just like dictionary access but with an optional default value.
6

Update dictionary records

For records that are dictionaries, use update() to modify specific fields:
# Update specific fields
db.update("user:1", {"age": 31, "city": "Madrid"})

# Check the result
print(db.get("user:1"))
# Output: {'name': 'Ana', 'age': 31, 'city': 'Madrid'}
The update() method only works with dictionary records. If you try to update a non-dictionary value, you’ll get a RecordTypeError.
7

Check existence and delete records

Use exists() to check if a key is present, and delete() to remove records:
# Check if a key exists
if db.exists("user:1"):
    print("User 1 exists")

# Delete a record
db.delete("counter")

# Verify deletion
print(db.exists("counter"))
# Output: False
8

Persist changes with commit()

Save all changes to disk:
db.commit()
This writes the entire database to disk atomically using a temporary file, preventing corruption if the write fails.
Changes are only stored in memory until you call commit(). If your program crashes before committing, unsaved changes will be lost.
9

Close the database

When you’re done, close the database:
db.close()
This commits any pending changes and marks the database as closed. After closing, you can’t perform any more operations on this instance.

Complete Example

Here’s a complete program that demonstrates the full workflow:
import os
from binarydb.database import Database

# Ensure the data directory exists
os.makedirs("./data", exist_ok=True)

# Create and load the database
db = Database("./data/mydb")
db.load()

# Add some data
db.set("user:1", {"name": "Ana", "age": 30})
db.set("user:2", {"name": "Carlos", "age": 25})
db.set("app_config", {"version": "1.0", "debug": False})

print(f"Database has {len(db)} records")
# Output: Database has 3 records

# Retrieve and display a user
user = db.get("user:1")
print(f"User 1: {user['name']}, age {user['age']}")
# Output: User 1: Ana, age 30

# Update a user
db.update("user:1", {"age": 31, "city": "Madrid"})
print(f"Updated user: {db.get('user:1')}")
# Output: Updated user: {'name': 'Ana', 'age': 31, 'city': 'Madrid'}

# List all keys
all_data = db.all()
print(f"All keys: {list(all_data.keys())}")
# Output: All keys: ['user:1', 'user:2', 'app_config']

# Save and close
db.commit()
db.close()

print("Database saved successfully!")

Working with Transactions

Transactions let you group multiple operations together and roll them back if something goes wrong:
from binarydb.database import Database

db = Database("./data/mydb")
db.load()

# Begin a transaction
db.begin()

try:
    # Make multiple changes
    db.set("account:1", {"balance": 1000})
    db.set("account:2", {"balance": 500})
    
    # Simulate a transfer
    acc1 = db.get("account:1")
    acc2 = db.get("account:2")
    
    transfer_amount = 100
    
    if acc1["balance"] >= transfer_amount:
        db.update("account:1", {"balance": acc1["balance"] - transfer_amount})
        db.update("account:2", {"balance": acc2["balance"] + transfer_amount})
        
        # Commit the transaction
        db.end()
        print("Transfer successful")
    else:
        # Rollback if insufficient funds
        db.rollback()
        print("Insufficient funds - transaction rolled back")
        
except Exception as e:
    # Rollback on any error
    db.rollback()
    print(f"Error: {e} - transaction rolled back")

db.close()
Transaction methods:
  • begin() - Start a transaction (creates a snapshot)
  • rollback() - Undo all changes since begin()
  • end() - Commit the transaction and persist to disk
Transactions are in-memory only. If your program crashes during a transaction, changes are lost. Always use end() to persist transaction changes.

Common Patterns

Context Manager Pattern

Create a simple context manager for automatic cleanup:
from contextlib import contextmanager

@contextmanager
def open_database(path):
    db = Database(path)
    db.load()
    try:
        yield db
    finally:
        db.close()

# Use it
with open_database("./data/mydb") as db:
    db.set("key", "value")
    print(db.get("key"))
# Database is automatically closed

Namespacing Keys

Use key prefixes to organize your data:
# User records
db.set("user:1", {...})
db.set("user:2", {...})

# Configuration
db.set("config:app", {...})
db.set("config:db", {...})

# Sessions
db.set("session:abc123", {...})

# Get all users
all_data = db.all()
users = {k: v for k, v in all_data.items() if k.startswith("user:")}

Checking Database State

Use the built-in Python protocols:
# Number of records
print(len(db))

# Check if key exists
if "user:1" in db:
    print("User 1 exists")

# String representation
print(db)
# Output: <Database path='./data/mydb.pkl' entries=5 state=open>

Error Handling

BinaryDB provides specific exceptions for different error conditions:
from binarydb.database import Database
from binarydb.errors import (
    DatabaseError,
    DatabaseIOError,
    DatabaseCorruptedError,
    KeyValidationError,
    RecordTypeError,
    TransactionError
)

db = Database("./data/mydb")

try:
    db.load()
    db.set("key", "value")
    db.commit()
except KeyValidationError:
    print("Invalid key format")
except RecordTypeError:
    print("Cannot perform operation on this record type")
except DatabaseIOError:
    print("Failed to read/write database file")
except DatabaseCorruptedError:
    print("Database file is corrupted")
except TransactionError:
    print("Invalid transaction operation")
except DatabaseError as e:
    print(f"Database error: {e}")
finally:
    db.close()

Best Practices

Always call load()

Call load() before working with a database to ensure you have the latest data from disk.

Commit regularly

Call commit() after important changes to prevent data loss if your program crashes.

Use transactions

Wrap related operations in transactions with begin() and end() for atomicity.

Close when done

Always call close() when finished to ensure pending changes are saved.
Memory considerations: All data is kept in memory. For large datasets, monitor your memory usage and consider alternative solutions if your data doesn’t fit comfortably in RAM.

Next Steps

Now that you understand the basics, explore more advanced features:
  • Learn about transaction handling in detail
  • Understand error handling and recovery
  • Explore the complete API reference
  • Check out real-world examples and patterns
BinaryDB is great for configuration storage, caching, session management, and small data processing tasks. Experiment with it in your next project!

Build docs developers (and LLMs) love