Use Cases (Atualizado: 02/06/2026)

Desenvolvimento Tauri com Claude Code: Vite, comandos Rust e permissoes

Guia pratico de Tauri v2 com Claude Code: Vite, Rust commands, invoke, permissoes de arquivos, testes e review.

Desenvolvimento Tauri com Claude Code: Vite, comandos Rust e permissoes

Tauri transforma uma UI web em app desktop para Windows, macOS e Linux. A tela pode continuar em React, Svelte ou outro stack frontend, enquanto as operacoes nativas ficam no Rust. Essa divisao combina bem com Claude Code porque permite pedir tarefas pequenas: uma tela, um Rust command, uma capability, um teste ou um ajuste de build.

Este guia usa Tauri v2 e um app simples de notas locais. Vamos cobrir quando escolher Tauri, setup com Vite + React ou Svelte, comandos Rust, chamadas invoke no frontend, permissoes de arquivos, comandos de build e teste, prompts para revisar permissoes, casos de uso reais e armadilhas comuns.

As referencias oficiais usadas foram Tauri Create a Project, Calling Rust from the Frontend, Tauri Capabilities, File System plugin, Vite Getting Started, Cargo test e Claude Code setup. Para base de Rust, leia Rust com Claude Code. Para comparar com Electron, veja Electron desktop app development.

Quando escolher Tauri

Tauri faz sentido quando voce precisa de um aplicativo local, mas quer manter a produtividade de uma UI web. Exemplos: notas privadas, conversor CSV interno, visualizador de logs, ferramenta para desenvolvedores, app offline de campo, inventario ou checklist.

Nao escolha Tauri apenas porque ele parece mais leve. Voce tambem assume toolchain Rust, empacotamento, assinatura, instaladores, diferencas entre sistemas operacionais, identificador do app e estrategia de atualizacao. Se o produto e basicamente uma web app com login e APIs remotas, uma web app normal ou PWA pode ser mais simples.

Um bom primeiro prompt para Claude Code:

Vamos criar um app Tauri v2 de notas locais.
Nao implemente ainda.
Divida o trabalho em React UI, Rust commands, capabilities e build/test.
O acesso a arquivos deve ficar dentro do diretorio app data.
Nao proponha leitura ou escrita de caminhos arbitrarios.

Assim o modelo discute a fronteira de seguranca antes de criar permissoes amplas.

Fronteira de arquitetura

No Tauri, o frontend nao deve tocar diretamente o sistema operacional. Ele chama comandos Rust com invoke; Rust valida a entrada, acessa o recurso local e retorna um resultado tipado.

flowchart LR
  UI["React ou Svelte UI"] --> Invoke["invoke from @tauri-apps/api/core"]
  Invoke --> Command["Rust command"]
  Command --> Guard["allowlist e validacao de caminho"]
  Guard --> AppData["diretorio app data"]
  Command --> Result["resultado tipado"]
  Result --> UI
  Capability["Tauri capability"] --> UI

O ponto que mais causa erro e: capabilities limitam APIs e plugins acessiveis pelo frontend, mas nao tornam seus comandos Rust automaticamente seguros. Se um command aceita qualquer caminho e escreve nele, a app continua perigosa. Capability e validacao Rust precisam ser revisadas juntas.

Setup com React ou Svelte

Para um projeto novo, use o caminho oficial create-tauri-app. Escolha React ou Svelte e TypeScript.

npm create tauri-app@latest taskdesk
cd taskdesk
npm install
npm run tauri dev

Se ja existe uma app Vite, prepare o frontend e inicialize Tauri depois. A documentacao do Vite mostra npm create vite@latest e, atualmente, exige Node.js 20.19+ ou 22.12+.

node --version
npm create vite@latest taskdesk -- --template react-ts
cd taskdesk
npm install
npm install -D @tauri-apps/cli@latest
npm install @tauri-apps/api@latest
npx tauri init
npx tauri dev

Para Svelte, troque apenas o template:

npm create vite@latest taskdesk -- --template svelte-ts

Depois do setup, peca para Claude Code ler antes de editar:

Leia package.json, tauri.conf.json e src-tauri/src/lib.rs.
Resuma a estrutura atual do projeto e os scripts.
Nao modifique arquivos ainda.

Templates mudam. Ler o projeto gerado reduz erros baseados em exemplos antigos.

Configuracao Tauri minima

tauri.conf.json conecta servidor Vite, build frontend, janela e capability.

{
  "$schema": "https://schema.tauri.app/config/2",
  "productName": "TaskDesk",
  "version": "0.1.0",
  "identifier": "com.example.taskdesk",
  "build": {
    "beforeDevCommand": "npm run dev",
    "devUrl": "http://localhost:5173",
    "beforeBuildCommand": "npm run build",
    "frontendDist": "../dist"
  },
  "app": {
    "windows": [
      {
        "title": "TaskDesk",
        "width": 1000,
        "height": 700
      }
    ],
    "security": {
      "capabilities": ["main-capability"]
    }
  },
  "bundle": {
    "active": true,
    "targets": "all"
  }
}

Revise identifier, devUrl e frontendDist. Esses campos costumam gerar debug inutil quando ficam desalinhados.

Comandos Rust

O app de notas precisa ler, escrever e listar. O exemplo foca na validacao: rejeitar caminho absoluto, .. e saida do app data.

// src-tauri/src/note_commands.rs
use serde::Serialize;
use std::{
    fs,
    path::{Component, Path, PathBuf},
};
use tauri::{AppHandle, Manager};

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct NoteFile {
    name: String,
    path: String,
    bytes: u64,
    is_dir: bool,
}

fn reject_unsafe_relative(path: &Path) -> Result<(), String> {
    for component in path.components() {
        match component {
            Component::Normal(_) | Component::CurDir => {}
            _ => return Err("use a relative path inside app data".to_string()),
        }
    }
    Ok(())
}

fn app_data_root(app: &AppHandle) -> Result<PathBuf, String> {
    let root = app
        .path()
        .app_data_dir()
        .map_err(|error| format!("failed to get app data dir: {error}"))?;
    fs::create_dir_all(&root).map_err(|error| format!("failed to create app data dir: {error}"))?;
    root.canonicalize()
        .map_err(|error| format!("failed to resolve app data dir: {error}"))
}

fn existing_path(app: &AppHandle, relative: &str) -> Result<PathBuf, String> {
    let root = app_data_root(app)?;
    let requested = Path::new(relative);
    reject_unsafe_relative(requested)?;
    let full = root
        .join(requested)
        .canonicalize()
        .map_err(|error| format!("path does not exist: {error}"))?;

    if !full.starts_with(&root) {
        return Err("path escapes app data".to_string());
    }

    Ok(full)
}

#[tauri::command]
pub fn read_note(app: AppHandle, path: String) -> Result<String, String> {
    let safe_path = existing_path(&app, &path)?;
    fs::read_to_string(safe_path).map_err(|error| format!("failed to read note: {error}"))
}

Registre no lib.rs:

// src-tauri/src/lib.rs
mod note_commands;

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![
            note_commands::read_note
        ])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

Invoke no TypeScript

Crie um wrapper pequeno para centralizar nomes e payloads.

// src/lib/notesApi.ts
import { invoke } from "@tauri-apps/api/core";

export type NoteFile = {
  name: string;
  path: string;
  bytes: number;
  isDir: boolean;
};

export const notesApi = {
  read(path: string) {
    return invoke<string>("read_note", { path });
  },
  write(path: string, content: string) {
    return invoke<void>("write_note", { path, content });
  },
  list(dir = ".") {
    return invoke<NoteFile[]>("list_notes", { dir });
  },
};

Um componente React minimo:

import { useState } from "react";
import { notesApi } from "./lib/notesApi";

export default function App() {
  const [content, setContent] = useState("");
  const [message, setMessage] = useState("Ready");

  async function saveNote() {
    await notesApi.write("daily-note.txt", content);
    setMessage("Saved");
  }

  return (
    <main>
      <textarea value={content} onChange={(event) => setContent(event.target.value)} />
      <button onClick={saveNote}>Save</button>
      <p>{message}</p>
    </main>
  );
}

Permissoes e capabilities

Se o frontend usa File System plugin diretamente, limite a capability:

{
  "$schema": "../gen/schemas/desktop-schema.json",
  "identifier": "main-capability",
  "description": "Main window permissions for TaskDesk.",
  "windows": ["main"],
  "permissions": [
    "core:default",
    "fs:default",
    "fs:allow-app-read-recursive",
    "fs:allow-app-write-recursive"
  ]
}

Neste artigo, arquivos passam por comandos Rust. Entao nao ha motivo para dar ao frontend permissoes amplas sobre Downloads ou Documents. Prompt de review:

Apenas revise, nao edite.
Inspecione src-tauri/capabilities e src-tauri/src/note_commands.rs.
Liste cada API exposta ao frontend e cada Rust command.
Explique quais caminhos de arquivo cada um pode tocar.
Marque caminhos absolutos, .., wildcards amplos e escrita fora de app data.
Sugira o menor conjunto de permissoes que mantem a funcionalidade.

Build, testes e casos reais

Separe os checks:

npm run build
cd src-tauri
cargo test
cd ..
npm run tauri build

npm run build valida Vite, cargo test valida Rust e npm run tauri build valida empacotamento desktop. Regras de caminho podem ser testadas em Rust puro:

#[cfg(test)]
mod tests {
    use super::reject_unsafe_relative;
    use std::path::Path;

    #[test]
    fn rejects_parent_directory() {
        assert!(reject_unsafe_relative(Path::new("../secret.txt")).is_err());
    }
}

Tres casos fortes sao notas locais, conversor CSV/Markdown e visualizador de logs. Um quarto e app offline de inventario, check-in ou inspecao. As armadilhas principais sao confundir capability com seguranca do Rust command, pedir a app inteira de uma vez ao Claude Code, levar configuracao de desenvolvimento para producao, testar em um unico OS e abrir permissoes de arquivo alem do necessario.

CTA e resultado testado

Para transformar isso em rotina, veja ClaudeCodeLab products and templates ou Claude Code training and consultation. Em equipes, a revisao precisa incluir CLAUDE.md, prompts de review, regras de capability e comandos de verificacao.

No fluxo testado para este artigo, a ordem mais estavel foi definir primeiro a fronteira de caminhos no Rust, depois criar o wrapper TypeScript de invoke e por ultimo revisar capabilities. Comecar pela UI parece rapido, mas aumenta retrabalho quando armazenamento e permissoes ficam claros.

#Claude Code #Tauri #Rust #desktop apps #frontend
Grátis

PDF grátis: cheatsheet do Claude Code

Informe seu e-mail e baixe uma página com comandos, hábitos de revisão e workflows seguros.

Cuidamos dos seus dados e não enviamos spam.

Masa

Sobre o autor

Masa

Engenheiro focado em workflows práticos com Claude Code.