Llamar Rust desde el Frontend

Por: Artiko
tauricomandosinvokerustipc

Llamar Rust desde el Frontend

Definir un comando

En src-tauri/src/lib.rs:

#[tauri::command]
fn greet(name: &str) -> String {
    format!("Hola, {}!", name)
}

Registralo en el builder:

pub fn run() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![greet])
        .run(tauri::generate_context!())
        .expect("error");
}

Invocar desde JavaScript

import { invoke } from '@tauri-apps/api/core';

const mensaje = await invoke('greet', { name: 'Artiko' });
console.log(mensaje); // "Hola, Artiko!"

Los argumentos se pasan como objeto con claves en camelCase. Tauri los convierte automaticamente al snake_case de Rust.

Argumentos multiples

#[tauri::command]
fn login(user: String, password: String) -> String {
    format!("Usuario: {}", user)
}
const result = await invoke('login', { user: 'admin', password: '123' });

Manejo de errores

Usa Result para retornar errores:

#[tauri::command]
fn divide(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 {
        Err("Division por cero".into())
    } else {
        Ok(a / b)
    }
}
try {
    const result = await invoke('divide', { a: 10, b: 0 });
} catch (error) {
    console.error(error); // "Division por cero"
}

Comandos asincronos

#[tauri::command]
async fn fetch_data(url: String) -> Result<String, String> {
    let response = reqwest::get(&url)
        .await
        .map_err(|e| e.to_string())?;
    response.text()
        .await
        .map_err(|e| e.to_string())
}

Desde JavaScript sigue siendo una promesa normal — no cambia nada.

Acceder al contexto

Estado de la app

struct AppConfig {
    api_url: String,
}

#[tauri::command]
fn get_api_url(state: tauri::State<AppConfig>) -> String {
    state.api_url.clone()
}

pub fn run() {
    tauri::Builder::default()
        .manage(AppConfig { api_url: "https://api.ejemplo.com".into() })
        .invoke_handler(tauri::generate_handler![get_api_url])
        .run(tauri::generate_context!())
        .expect("error");
}

Ventana actual

#[tauri::command]
async fn get_label(window: tauri::WebviewWindow) -> String {
    window.label().to_string()
}

AppHandle

#[tauri::command]
async fn get_app_dir(app: tauri::AppHandle) -> Result<String, String> {
    let dir = app.path().app_data_dir()
        .map_err(|e| e.to_string())?;
    Ok(dir.to_string_lossy().to_string())
}

Comandos en modulos separados

src-tauri/src/commands.rs:

#[tauri::command]
pub fn my_command() -> String {
    "Desde modulo separado".into()
}

src-tauri/src/lib.rs:

mod commands;

pub fn run() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![commands::my_command])
        .run(tauri::generate_context!())
        .expect("error");
}

Canales para streaming

Para transmitir datos en chunks (progreso de descarga, logs en tiempo real):

use tauri::ipc::Channel;

#[tauri::command]
async fn download(url: String, on_progress: Channel<u32>) {
    for i in 0..=100 {
        on_progress.send(i).unwrap();
        tokio::time::sleep(std::time::Duration::from_millis(50)).await;
    }
}
await invoke('download', {
    url: 'https://ejemplo.com/archivo.zip',
    onProgress: new Channel((progress) => {
        console.log(`${progress}%`);
    })
});

Resumen


← Arquitectura | Indice | Siguiente: Llamar al Frontend desde Rust →