For years, SQLite has been my go-to solution for storing any kind of structured local data for apps. The ability to update multiple pieces of data in a transaction has been extremely useful.
Naturally, when building my app in Tauri, I wanted to use SQLite from my Rust backend code.
A bit of poking around led to Rusqlite for an ergonomic SQLite wrapper.
But there's a couple other questions to answer in order to tie it in with Tauri.
Where do you store the SQLite file?
Inside setup()
we can create an app handle and call app_handle.path_resolver().app_data_dir()
. That's the designated place to store our app data. From there we can initialize our DB file.
How do we access the Connection
from a Tauri command?
Tauri has a built-in ability to store app state. We'll set up struct to hold it:
pub struct AppState {
pub db: std::sync::Mutex<Option<Connection>>,
}
The Mutex protects access to the connection object. The idea is that you get the Connection
, run a query on it, then release the Mutex.
In setup()
we can populate this app state with the DB connection.
let app_state: State<AppState> = handle.state();
*app_state.db.lock().unwrap() = Some(db);
We can also add a convenience trait to easily access the Connection
object without jumping through the State
fetch and Mutex
lock.
pub trait ServiceAccess {
fn db<F, TResult>(&self, operation: F) -> TResult where F: FnOnce(&Connection) -> TResult;
}
impl ServiceAccess for AppHandle {
fn db<F, TResult>(&self, operation: F) -> TResult where F: FnOnce(&Connection) -> TResult {
let app_state: State<AppState> = self.state();
let db_connection_guard = app_state.db.lock().unwrap();
let db = db_connection_guard.as_ref().unwrap();
operation(db)
}
}
Inside Tauri commands we can supply a app_handle: AppHandle
argument, which will be automatically populated by the Tauri framework. From there we can call our db()
trait method on app_handle
to do database operations. Anything returned from the closure will be passed through.
let my_result = app_handle.db(|db: &Connection| {
/* Do something with the DB connection and return a value */
});
I pass the AppHandle
through to any other modules that need it, so they also have access to the Connection
. You can also call app_handle.clone()
in case you run into ownership issues.
Top comments (3)
Tauri has a sqlite plugin at "github.com/tauri-apps/tauri-plugin...".
Could you make a post using the plugin as an update to this?
github.com/tauri-apps/plugins-work...
tauri plugin for sql haven't the feature yet to be called from backend side, hope this will be implemented soon
Exactly, this is the main reason I'm not using tauri plugin.
Nice article, it was very helpful.