A todo and personal organisation app
Database Schema#
Overview#
Toadist uses PostgreSQL 16+ for persistent storage.
Tables#
users#
Stores user accounts.
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
username VARCHAR(255) UNIQUE NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
| Column | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| username | VARCHAR(255) | Unique username |
| VARCHAR(255) | Unique email | |
| password_hash | VARCHAR(255) | bcrypt hash |
| created_at | TIMESTAMP | Account creation |
| updated_at | TIMESTAMP | Last update |
noots#
Stores noot containers.
CREATE TABLE noots (
id UUID PRIMARY KEY,
user_id UUID NOT NULL REFERENCES users(id),
version INTEGER NOT NULL DEFAULT 1,
checksum VARCHAR(64) NOT NULL,
data JSONB NOT NULL,
deleted BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_noots_user_id ON noots(user_id);
CREATE INDEX idx_noots_checksum ON noots(checksum);
CREATE INDEX idx_noots_deleted ON noots(deleted);
| Column | Type | Description |
|---|---|---|
| id | UUID | Noot ID (from client) |
| user_id | UUID | Owner reference |
| version | INTEGER | Conflict resolution |
| checksum | VARCHAR(64) | SHA-256 hash |
| data | JSONB | Serialized activities |
| deleted | BOOLEAN | Soft delete flag |
| created_at | TIMESTAMP | Creation time |
| updated_at | TIMESTAMP | Last modification |
refresh_tokens#
Stores active refresh tokens.
CREATE TABLE refresh_tokens (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id),
token VARCHAR(512) NOT NULL,
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_refresh_tokens_user_id ON refresh_tokens(user_id);
CREATE INDEX idx_refresh_tokens_token ON refresh_tokens(token);
JSONB Data Structure#
The data column in noots stores:
{
"activities": [
{
"activityType": "todo",
"properties": {
"title": "Buy groceries",
"completed": false,
"priority": "high"
},
"baseProperties": {
"encrypted": false,
"syncPriority": "normal"
}
}
]
}
Migrations#
Migrations run automatically on server startup via DatabaseService.initialize().
Adding New Migrations#
- Add SQL to
DatabaseService._runMigrations() - Use
IF NOT EXISTSfor idempotency - Test with fresh database
Queries#
Common Patterns#
// Get user's noots
final noots = await db.getNootsByUser(userId);
// Get noot by ID
final noot = await db.getNoot(nootId);
// Upsert noot
await db.upsertNoot(userId, nootData);
// Get checksums for sync
final checksums = await db.getChecksums(userId);
Backup and Restore#
Creating Backups#
Use the backup_db.sh script to create timestamped, compressed backups:
# Create backup in default 'backups/' directory
./scripts/backup_db.sh
# Create backup in custom directory
./scripts/backup_db.sh /path/to/backup/directory
The script will:
- Automatically detect Docker or local PostgreSQL
- Create a timestamped backup file:
toadist_backup_YYYY-MM-DD_HH-MM-SS.sql.gz - Compress the backup using gzip
Restoring from Backup#
Use the restore_db.sh script to restore from a backup file:
./scripts/restore_db.sh backups/toadist_backup_2026-02-09_23-15-00.sql.gz
The script will:
- Prompt for confirmation before overwriting data
- Automatically detect Docker or local PostgreSQL
- Handle both compressed (.gz) and uncompressed (.sql) files
WARNING
Restoring a backup will overwrite all existing data in the database. Always confirm you have the correct backup file before proceeding.
Best Practices#
- Regular backups: Schedule regular backups using cron or similar tools
- Backup retention: Keep multiple backup versions (daily, weekly, monthly)
- Test restores: Periodically test backup restoration to ensure backups are valid
- Secure storage: Store backups in a secure location separate from the database server
- Before updates: Always create a backup before major updates or migrations