tauri/docs/guides/command.md
Kasper 4f9c9e6307
Restructure docs (#3180)
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
2022-01-07 09:30:23 -03:00

6.2 KiB

title
Create Rust Commands

import Alert from '@theme/Alert'

Tauri provides a simple yet powerful "command" system for calling Rust functions from your web app. Commands can accept arguments and return values. They can also return errors and be async.

Basic Example

Commands are defined in your src-tauri/src/main.rs file. To create a command, just add a function and annotate it with #[tauri::command]:

#[tauri::command]
fn my_custom_command() {
  println!("I was invoked from JS!");
}

You will have to provide a list of your commands to the builder function like so:

// Also in main.rs
fn main() {
  tauri::Builder::default()
    // This is where you pass in your commands
    .invoke_handler(tauri::generate_handler![my_custom_command])
    .run(tauri::generate_context!())
    .expect("failed to run app");
}

Now, you can invoke the command from your JS code:

// With the Tauri API npm package:
import { invoke } from '@tauri-apps/api/tauri'
// With the Tauri global script, enabled when `tauri.conf.json > build > withGlobalTauri` is set to true:
const invoke = window.__TAURI__.invoke

// Invoke the command
invoke('my_custom_command')

Passing Arguments

Your command handlers can take arguments:

#[tauri::command]
fn my_custom_command(invoke_message: String) {
  println!("I was invoked from JS, with this message: {}", invoke_message);
}

Arguments should be passed as a JSON object with camelCase keys:

invoke('my_custom_command', { invokeMessage: 'Hello!' })

Arguments can be of any type, as long as they implement serde::Deserialize.

Returning Data

Command handlers can return data as well:

#[tauri::command]
fn my_custom_command() -> String {
  "Hello from Rust!".into()
}

The invoke function returns a promise that resolves with the returned value:

invoke('my_custom_command').then((message) => console.log(message))

Returned data can be of any type, as long as it implements Serde::Serialize.

Error Handling

If your handler could fail and needs to be able to return an error, have the function return a Result:

#[tauri::command]
fn my_custom_command() -> Result<String, String> {
  // If something fails
  Err("This failed!".into())
  // If it worked
  Ok("This worked!".into())
}

If the command returns an error, the promise will reject, otherwise it resolves:

invoke('my_custom_command')
  .then((message) => console.log(message))
  .catch((error) => console.error(error))

Async Commands

Async commands are executed on a separate thread using the async runtime. Commands without the async keyword are executed on the main thread, unless defined with #[tauri::command(async)].

If your command needs to run asynchronously, simply declare it as async:

#[tauri::command]
async fn my_custom_command() {
  // Call another async function and wait for it to finish
  let result = some_async_function().await;
  println!("Result: {}", result);
}

Since invoking the command from JS already returns a promise, it works just like any other command:

invoke('my_custom_command').then(() => console.log('Completed!'))

Accessing the Window in Commands

Commands can access the Window instance that invoked the message:

#[tauri::command]
async fn my_custom_command(window: tauri::Window) {
  println!("Window: {}", window.label());
}

Accessing an AppHandle in Commands

Commands can access an AppHandle instance:

#[tauri::command]
async fn my_custom_command(app_handle: tauri::AppHandle) {
  let app_dir = app_handle.path_resolver().app_dir();
  use tauri::GlobalShortcutManager;
  app_handle.global_shortcut_manager().register("CTRL + U", move || {});
}

Accessing managed state

Tauri can manage state using the manage function on tauri::Builder. The state can be accessed on a command using tauri::State:

struct MyState(String);

#[tauri::command]
fn my_custom_command(state: tauri::State<MyState>) {
  assert_eq!(state.0 == "some state value", true);
}

fn main() {
  tauri::Builder::default()
    .manage(MyState("some state value".into()))
    .invoke_handler(tauri::generate_handler![my_custom_command])
    .run(tauri::generate_context!())
    .expect("error while running tauri application");
}

Creating Multiple Commands

The tauri::generate_handler! macro takes an array of commands. To register multiple commands, you cannot call invoke_handler multiple times. Only the last call will be used. You must pass each command to a single call of tauri::generate_handler!.

#[tauri::command]
fn cmd_a() -> String {
	"Command a"
}
#[tauri::command]
fn cmd_b() -> String {
	"Command b"
}

fn main() {
  tauri::Builder::default()
    .invoke_handler(tauri::generate_handler![cmd_a, cmd_b])
    .run(tauri::generate_context!())
    .expect("error while running tauri application");
}

Complete Example

Any or all of the above features can be combined:

// Definition in main.rs

struct Database;

#[derive(serde::Serialize)]
struct CustomResponse {
  message: String,
  other_val: usize,
}

async fn some_other_function() -> Option<String> {
  Some("response".into())
}

#[tauri::command]
async fn my_custom_command(
  window: tauri::Window,
  number: usize,
  database: tauri::State<'_, Database>,
) -> Result<CustomResponse, String> {
  println!("Called from {}", window.label());
  let result: Option<String> = some_other_function().await;
  if let Some(message) = result {
    Ok(CustomResponse {
      message,
      other_val: 42 + number,
    })
  } else {
    Err("No result".into())
  }
}

fn main() {
  tauri::Builder::default()
    .manage(Database {})
    .invoke_handler(tauri::generate_handler![my_custom_command])
    .run(tauri::generate_context!())
    .expect("error while running tauri application");
}
// Invocation from JS

invoke('my_custom_command', {
  number: 42,
})
  .then((res) =>
    console.log(`Message: ${res.message}, Other Val: ${res.other_val}`)
  )
  .catch((e) => console.error(e))