1

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 Answer 1

0

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.