kit_rs/database/
mod.rs

1//! Database module for Kit framework
2//!
3//! Provides a SeaORM-based ORM with Laravel-like API.
4//!
5//! # Quick Start
6//!
7//! ```rust,ignore
8//! use kit::{Config, DB, DatabaseConfig};
9//!
10//! // 1. Register database config (in config/mod.rs)
11//! Config::register(DatabaseConfig::from_env());
12//!
13//! // 2. Initialize connection (in bootstrap.rs)
14//! DB::init().await.expect("Failed to connect to database");
15//!
16//! // 3. Use in controllers
17//! let conn = DB::connection()?;
18//! let users = User::find().all(conn.inner()).await?;
19//! ```
20//!
21//! # Configuration
22//!
23//! Set these environment variables:
24//!
25//! ```env
26//! DATABASE_URL=postgres://user:pass@localhost:5432/mydb
27//! # or for SQLite:
28//! DATABASE_URL=sqlite://./database.db
29//!
30//! # Optional:
31//! DB_MAX_CONNECTIONS=10
32//! DB_MIN_CONNECTIONS=1
33//! DB_CONNECT_TIMEOUT=30
34//! DB_LOGGING=false
35//! ```
36
37pub mod config;
38pub mod connection;
39pub mod model;
40pub mod route_binding;
41
42pub use config::{DatabaseConfig, DatabaseConfigBuilder, DatabaseType};
43pub use connection::DbConnection;
44pub use model::{Model, ModelMut};
45pub use route_binding::{AutoRouteBinding, RouteBinding};
46
47/// Injectable database connection type
48///
49/// This is an alias for `DbConnection` that can be used with dependency injection.
50/// Use with the `#[inject]` attribute in actions and services for cleaner database access.
51///
52/// # Example
53///
54/// ```rust,ignore
55/// use kit::{injectable, Database};
56///
57/// #[injectable]
58/// pub struct CreateUserAction {
59///     #[inject]
60///     db: Database,
61/// }
62///
63/// impl CreateUserAction {
64///     pub async fn execute(&self) {
65///         let users = User::find().all(self.db.conn()).await?;
66///     }
67/// }
68/// ```
69pub type Database = DbConnection;
70
71use crate::error::FrameworkError;
72use crate::{App, Config};
73
74/// Database facade - main entry point for database operations
75///
76/// Provides static methods for initializing and accessing the database connection.
77/// The connection is stored in the application container as a singleton.
78///
79/// # Example
80///
81/// ```rust,ignore
82/// use kit::{DB, DatabaseConfig, Config};
83///
84/// // Initialize (usually in bootstrap.rs)
85/// Config::register(DatabaseConfig::from_env());
86/// DB::init().await?;
87///
88/// // Use anywhere in your app
89/// let conn = DB::connection()?;
90/// ```
91pub struct DB;
92
93impl DB {
94    /// Initialize the database connection
95    ///
96    /// Reads configuration from `DatabaseConfig` (must be registered via Config system)
97    /// and establishes a connection pool. The connection is stored in the App container.
98    ///
99    /// # Errors
100    ///
101    /// Returns an error if:
102    /// - `DatabaseConfig` is not registered
103    /// - Connection to the database fails
104    ///
105    /// # Example
106    ///
107    /// ```rust,ignore
108    /// // In bootstrap.rs
109    /// pub async fn register() {
110    ///     DB::init().await.expect("Failed to connect to database");
111    /// }
112    /// ```
113    pub async fn init() -> Result<(), FrameworkError> {
114        let config = Config::get::<DatabaseConfig>().ok_or_else(|| {
115            FrameworkError::internal(
116                "DatabaseConfig not registered. Call Config::register(DatabaseConfig::from_env()) first.",
117            )
118        })?;
119
120        let connection = DbConnection::connect(&config).await?;
121        App::singleton(connection);
122        Ok(())
123    }
124
125    /// Initialize with a custom config
126    ///
127    /// Useful for testing or when you need to connect to a different database.
128    ///
129    /// # Example
130    ///
131    /// ```rust,ignore
132    /// let config = DatabaseConfig::builder()
133    ///     .url("sqlite::memory:")
134    ///     .build();
135    /// DB::init_with(config).await?;
136    /// ```
137    pub async fn init_with(config: DatabaseConfig) -> Result<(), FrameworkError> {
138        let connection = DbConnection::connect(&config).await?;
139        App::singleton(connection);
140        Ok(())
141    }
142
143    /// Get the database connection
144    ///
145    /// Returns the connection from the App container. The connection is wrapped
146    /// in a `DbConnection` which provides convenient access to the underlying
147    /// SeaORM `DatabaseConnection`.
148    ///
149    /// # Errors
150    ///
151    /// Returns an error if `DB::init()` was not called.
152    ///
153    /// # Example
154    ///
155    /// ```rust,ignore
156    /// let conn = DB::connection()?;
157    ///
158    /// // Use with SeaORM queries
159    /// let users = User::find()
160    ///     .all(conn.inner())
161    ///     .await?;
162    /// ```
163    pub fn connection() -> Result<DbConnection, FrameworkError> {
164        App::resolve::<DbConnection>()
165    }
166
167    /// Check if the database connection is initialized
168    ///
169    /// # Example
170    ///
171    /// ```rust,ignore
172    /// if DB::is_connected() {
173    ///     let conn = DB::connection()?;
174    ///     // ...
175    /// }
176    /// ```
177    pub fn is_connected() -> bool {
178        App::has::<DbConnection>()
179    }
180
181    /// Get the database connection for use with SeaORM
182    ///
183    /// This is a convenience alias for `DB::connection()`. The returned
184    /// `DbConnection` implements `Deref<Target=DatabaseConnection>`, so you
185    /// can use it directly with SeaORM methods.
186    ///
187    /// # Example
188    ///
189    /// ```rust,ignore
190    /// use kit::database::DB;
191    /// use sea_orm::{Set, ActiveModelTrait};
192    ///
193    /// let new_todo = todos::ActiveModel {
194    ///     title: Set("My Todo".to_string()),
195    ///     ..Default::default()
196    /// };
197    ///
198    /// // Use &* to dereference to &DatabaseConnection
199    /// let inserted = new_todo.insert(&*DB::get()?).await?;
200    ///
201    /// // Or use .inner() method
202    /// let inserted = new_todo.insert(DB::get()?.inner()).await?;
203    /// ```
204    pub fn get() -> Result<DbConnection, FrameworkError> {
205        Self::connection()
206    }
207}
208
209// Re-export sea_orm types that users commonly need
210pub use sea_orm;