Tauri and shared postgres instance

160 Views Asked by At

I'm currently developing an app where it is necessary for the frontend to communicate with a PostgreSQL database. I'm using the postgres package to do the PostgreSQL work on the Rust side.

Since I'm using Tauri here, I was building commands to create the connection and delivering data to the user when it's requested.

My problem here is, that I cannot share the PostgreSQL Client between the commands. I know that there is some sort of tauri::State but that cannot handle the client.

Is there a way to share the postgres::Client instance between multiple commands?

My current code looks something like this:

#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

use postgres::Client;

struct ClientState(Option<Client>);

#[tauri::command]
fn connect(client_state: tauri::State<ClientState>) { ... }

fn main() {
    tauri::Builder::default()
        .manage(ClientState(None))
        .invoke_handler(tauri::generate_handler![connect])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

I want the code to share the instance of the client

#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

use postgres::Client;

struct ClientState(Option<Client>);

#[tauri::command]
fn connect(client_state: tauri::State<ClientState>) {
    // initialize the client
}

#[tauri::command]
fn exists_user(id: &str, client_state: tauri::State<ClientState>) -> bool {
    // check if a user with the given id exists for example    
}

fn main() {
    tauri::Builder::default()
        .manage(ClientState(None))
        .invoke_handler(tauri::generate_handler![connect, exists_user])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
1

There are 1 best solutions below

0
On BEST ANSWER

Hopefully I am not too late for your use-case. I am building the exact same pattern that you are, and I had the same problem. I indeed ended up using tauri::State.

Firstly, my original application uses sqlx::PgConnection and a bunch of async stuff, but I tested with postgres and it should actually end up simpler.

After much trial and error, I now instantiate my global structs like this:

struct DbConnection {
    client: Mutex<Option<Client>>
}

Afterwards, you can use your client like this:

#[tauri::command]
fn exec(
    connection: State<DbConnection>,
    sql: &str
) -> u64 {
    let mut binding = connection.client.lock().unwrap();
    let client = binding.as_mut().unwrap();
    // You can start using your `postgres::Client` here
    let result = client.execute(sql, &[]).unwrap();
    return result;

Of course, this doesn't work without initialization. I prefer to not actually initialize the connection at start up, but use a separate function to do it.

I use core::default for setting default values in .manage(), that will never be used, but are useful for going on with the initialization:

    tauri::Builder::default()
        .manage(DbConnection {
            client: Default::default()
        })
        ...

and then in a function, that is actually another tauri command:

#[tauri::command]
fn init(connection: State<DbConnection>) -> Result<(), String> {
    *connection.client.lock().unwrap() = Some(Client::connect("host=localhost user=postgres", NoTls).unwrap());
}

I wrote all this in my app, to see if it works, and it connected correctly, but I didn't test any actual sql. You can see my app here, for further inspiration.