Rustelo/docs/database_support_summary.md
Jesús Pérex 2f0f807331 feat: add dark mode functionality and improve navigation system
- Add complete dark mode system with theme context and toggle
- Implement dark mode toggle component in navigation menu
- Add client-side routing with SSR-safe signal handling
- Fix language selector styling for better dark mode compatibility
- Add documentation system with mdBook integration
- Improve navigation menu with proper external/internal link handling
- Add comprehensive project documentation and configuration
- Enhance theme system with localStorage persistence
- Fix arena panic issues during server-side rendering
- Add proper TypeScript configuration and build optimizations

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-11 20:53:20 +01:00

7.2 KiB

Database Support Summary

This document summarizes the database support implementation in the Rust application, which supports both PostgreSQL and SQLite databases based on the connection URL.

Current Implementation Status

What Works

  1. URL-Based Database Detection: The application automatically detects database type from the connection URL
  2. PostgreSQL Support: Full support for PostgreSQL with native SQL syntax
  3. SQLite Configuration: Configuration files support SQLite URLs
  4. Unified Codebase: Single codebase handles both database types
  5. Automatic Table Creation: Database tables are created automatically on startup

🔧 Current Limitations

  1. PostgreSQL-First Implementation: The current code uses PostgreSQL-specific SQL syntax
  2. SQLite Compatibility: While SQLx supports SQLite, the SQL queries use PostgreSQL syntax ($1, $2) instead of SQLite syntax (?1, ?2)
  3. Type Handling: Uses PostgreSQL-specific types (UUID, TIMESTAMPTZ, JSONB)

How It Works

Database Connection

The application detects the database type from the DATABASE_URL environment variable:

// In server/src/main.rs
let db_type = if database_url.starts_with("postgres://") 
    || database_url.starts_with("postgresql://") {
    "PostgreSQL"
} else if database_url.starts_with("sqlite:") {
    "SQLite"
} else {
    "Unknown"
};

Current Database Pool

The application currently uses PgPool throughout, which works with PostgreSQL URLs directly and can be configured to work with SQLite through SQLx's compatibility layer.

// Uses PgPool but can connect to SQLite URLs
PgPool::connect(database_url).await

Configuration Examples

PostgreSQL Configuration

[database]
url = "postgresql://username:password@localhost:5432/database_name"
max_connections = 10
min_connections = 1

SQLite Configuration

[database]
url = "sqlite:database.db"
max_connections = 10
min_connections = 1

Environment Variable Override

# PostgreSQL
export DATABASE_URL="postgresql://user:pass@localhost:5432/mydb"

# SQLite
export DATABASE_URL="sqlite:my_app.db"

Practical Usage

Quick Start with SQLite

export DATABASE_URL="sqlite:database.db"
cargo run --bin server

Quick Start with PostgreSQL

# Start PostgreSQL with Docker
docker run -d --name postgres -e POSTGRES_PASSWORD=password -p 5432:5432 postgres:15

# Connect to it
export DATABASE_URL="postgresql://postgres:password@localhost:5432/postgres"
cargo run --bin server

Switching Between Databases

# Start with SQLite
export DATABASE_URL="sqlite:test.db"
cargo run --bin server

# Stop server (Ctrl+C), switch to PostgreSQL
export DATABASE_URL="postgresql://localhost:5432/mydb"
cargo run --bin server

Implementation Architecture

Current Structure

├── server/src/main.rs           # Database connection logic
├── server/src/auth/repository.rs   # Authentication database operations
├── server/src/content/repository.rs # Content management database operations
└── server/src/auth/two_factor.rs   # 2FA database operations

Database Abstraction Pattern

The code currently uses a direct PgPool approach with PostgreSQL syntax, but is structured to allow for database-specific implementations in the future.

Future Enhancement Options

Create a DatabasePool enum that handles both database types with appropriate SQL syntax:

pub enum DatabasePool {
    Postgres(PgPool),
    Sqlite(SqlitePool),
}

Option 2: Conditional Compilation

Use feature flags to compile for specific databases:

#[cfg(feature = "postgres")]
use sqlx::PgPool as DbPool;
#[cfg(feature = "sqlite")]
use sqlx::SqlitePool as DbPool;

Option 3: SQLx Any Driver

Use SQLx's Any driver for complete database agnosticism (with some type limitations).

Database Schema Compatibility

PostgreSQL Schema Features

  • UUID primary keys with gen_random_uuid()
  • TIMESTAMPTZ for timestamps
  • JSONB for JSON data
  • Advanced indexing

SQLite Equivalent Features

  • TEXT UUIDs with custom generation
  • DATETIME for timestamps
  • TEXT for JSON storage
  • Standard indexing

Performance Considerations

PostgreSQL

  • Best for: Production, multi-user, high concurrency
  • Features: Full ACID, advanced indexing, network access
  • Connection Pool: 10-50 connections typical

SQLite

  • Best for: Development, testing, single-user apps
  • Features: File-based, zero configuration, embedded
  • Connection Pool: 1 connection recommended

Security Considerations

PostgreSQL

  • Network security (firewall, VPN)
  • Authentication (strong passwords, SSL)
  • Authorization (role-based access)
  • Regular security updates

SQLite

  • File system permissions (600/640)
  • Backup encryption
  • Access control at application level

Testing Strategy

Unit Tests

# PostgreSQL tests
export DATABASE_URL="postgresql://localhost/test_db"
cargo test

# SQLite tests  
export DATABASE_URL="sqlite::memory:"
cargo test

Integration Tests

The application includes integration tests that can run against both database types by setting the appropriate DATABASE_URL.

Migration Strategy

From SQLite to PostgreSQL

  1. Export data from SQLite
  2. Set up PostgreSQL instance
  3. Change DATABASE_URL
  4. Import data to PostgreSQL
  5. Restart application

From PostgreSQL to SQLite

  1. Export data from PostgreSQL
  2. Change DATABASE_URL to SQLite
  3. Restart application (tables created automatically)
  4. Import data to SQLite

Troubleshooting

Common Issues

"spawn_local called from outside of a task::LocalSet"

  • Fixed: The application uses proper LocalSet configuration

Database connection failed

  • Check if database server is running
  • Verify connection string format
  • Check network access and credentials

Table creation errors

  • Ensure database user has CREATE privileges
  • Check for existing incompatible tables
  • Review logs for specific SQL errors

Debug Commands

# Test database connection
psql $DATABASE_URL -c "SELECT 1;"  # PostgreSQL
sqlite3 database.db "SELECT 1;"    # SQLite

# Check application logs
RUST_LOG=debug cargo run --bin server

Best Practices

  1. Use environment variables for database URLs in production
  2. Start with SQLite for development and testing
  3. Use PostgreSQL for production deployments
  4. Configure appropriate connection pools for your workload
  5. Monitor database performance and adjust settings
  6. Regular backups for production databases
  7. Test migrations on both database types if needed

Conclusion

The current implementation provides a solid foundation for supporting both PostgreSQL and SQLite databases. While the code currently uses PostgreSQL syntax throughout, the architecture is designed to accommodate both databases through URL-based configuration.

For most use cases:

  • Development: Use SQLite for simplicity
  • Production: Use PostgreSQL for scalability and features
  • Testing: Use in-memory SQLite for speed

The database choice can be changed at any time by simply updating the DATABASE_URL, making it easy to evolve your database strategy as your application grows.