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