skills/cacr92/wereply/tauri-development

tauri-development

SKILL.md

Tauri Development Skill

Expert guidance for Tauri 2.0 desktop application development with Rust backend and React TypeScript frontend.

Overview

This skill provides comprehensive guidance for:

  • Creating Tauri commands with proper specta type binding
  • Integrating Rust backend with React TypeScript frontend
  • Handling desktop-specific patterns and constraints
  • Optimizing frontend-backend communication
  • Following Tauri 2.0 best practices

When This Skill Applies

This skill activates when:

  • Creating or modifying Tauri commands
  • Setting up specta type bindings
  • Debugging frontend-backend communication issues
  • Implementing desktop-specific features
  • Optimizing Tauri application performance
  • Handling Tauri-specific error cases

Core Tauri Principles

Command Definition Pattern

All Tauri commands must follow this pattern:

use serde::{Deserialize, Serialize};
use specta::Type;

#[derive(Serialize, Deserialize, Type, Clone)]
#[specta(inline)]
pub struct MyDto {
    pub field: String,
}

#[tauri::command]
#[specta::specta]
pub async fn my_command(
    dto: MyDto,
    state: State<'_, TauriAppState>,
) -> ApiResponse<ResultType> {
    // Implementation
}

Key Requirements:

  • #[tauri::command] - Required for all commands
  • #[specta::specta] - Required for TypeScript type generation
  • #[specta(inline)] - Required on DTOs for proper type export
  • ApiResponse<T> - Unified return type for error handling

Frontend Command Usage

✓ Correct:

import { commands } from '../bindings';

const result = await commands.myCommand({ field: "value" });
if (!result.success) {
    message.error(result.message);
    return;
}
const data = result.data;

✗ Incorrect:

// ✗ Don't use raw invoke
import { invoke } from '@tauri-apps/api/core';
const result = await invoke('my_command', { dto });

Common Patterns

State Management

Use dependency injection for services:

pub struct TauriAppState {
    pub db: Arc<SqlitePool>,
    pub material_service: Arc<MaterialService>,
}

pub async fn get_state() -> TauriAppState {
    TauriAppState {
        db: Arc::new(pool),
        material_service: Arc::new(MaterialService::new(pool)),
    }
}

Error Handling

Always use ApiResponse for top-level commands:

use crate::utils::error::{api_err, api_ok, ApiResponse};

#[tauri::command]
#[specta::specta]
pub async fn some_command() -> ApiResponse<Data> {
    match inner_logic().await {
        Ok(data) => api_ok(data),
        Err(e) => api_err(format!("操作失败: {}", e)),
    }
}

Async/Await Best Practices

  • Use async for external-facing commands
  • Use sync functions for pure computations
  • Never block the async runtime with std::thread::sleep
  • Use tokio::time::sleep instead

Tauri-Specific Considerations

Desktop Application Constraints

  1. No console.log - Use Ant Design message components
  2. File paths - Use path.resolve for cross-platform compatibility
  3. Native dialogs - Use Tauri APIs for file dialogs
  4. System integration - Leverage native OS features

Performance Optimization

  • Use moka::future::Cache for frequently accessed data
  • Implement proper connection pooling for SQLite
  • Use rayon for CPU-intensive parallel computations
  • Minimize bridge calls between frontend and backend

Security Considerations

  • Validate all user input on both frontend and backend
  • Use SQL parameter binding (never string concatenation)
  • Sanitize file paths to prevent directory traversal
  • Implement proper error handling without exposing sensitive info

Common Pitfalls

❌ Blocking Async Runtime

// ✗ Blocks entire runtime
pub async fn bad() {
    std::thread::sleep(Duration::from_secs(1));
}

✅ Proper Async Sleep

// ✓ Async-friendly
pub async fn good() {
    tokio::time::sleep(Duration::from_secs(1)).await;
}

❌ SELECT * in Queries

// ✗ Inefficient, unclear
sqlx::query_as!("SELECT * FROM materials WHERE code = ?", code)

✅ Explicit Column Selection

// ✓ Clear intent, optimized
sqlx::query_as!(
    Material,
    "SELECT code, name, price FROM materials WHERE code = ?",
    code
)

Type Generation Workflow

  1. Define Rust types with #[specta::specta] and #[specta(inline)]
  2. Run npm run tauri build or development server
  3. Types are automatically generated in frontend/src/bindings.ts
  4. Import and use types in TypeScript code

Testing Guidelines

Backend Testing

  • Unit tests for pure functions
  • Integration tests for database operations
  • Use test database fixtures

Frontend Testing

  • Test command success/error paths
  • Mock Tauri commands in tests
  • Verify type safety across the bridge

When to Use This Skill

Activate this skill when:

  • Creating or modifying Tauri commands
  • Debugging frontend-backend communication
  • Optimizing Tauri application performance
  • Handling Tauri-specific error cases
  • Planning new features with Tauri constraints

Quick Reference

Essential Tauri Command Template

#[tauri::command]
#[specta::specta]
pub async fn command_name(
    dto: RequestDto,
    state: State<'_, TauriAppState>,
) -> ApiResponse<ResponseData> {
    with_service(state, |ctx| async move {
        ctx.service.do_work(dto).await
    })
    .await
}

Frontend Command Call Template

import { commands } from '../bindings';

const result = await commands.commandName({ field: "value" });
if (!result.success) {
    message.error(result.message);
    return;
}
const data = result.data;

Required Attributes Checklist

  • #[tauri::command] on all commands
  • #[specta::specta] on all commands
  • #[specta(inline)] on all DTOs
  • Return type ApiResponse<T>
  • Use commands from bindings (not invoke)
  • Handle errors with message component
  • No console.log in desktop app

Common Issues & Solutions

Issue Solution
Types not generated Run dev server or rebuild
Command not found Check #[tauri::command] attribute
Type mismatch Verify #[specta(inline)] on DTOs
Runtime blocking Use async/await, not blocking calls
Console logs in app Use message component instead

Frontmatter Fields Summary

Field Value Purpose
#[tauri::command] - Exposes function to frontend
#[specta::specta] - Enables type generation
#[specta(inline)] - Inlines type in bindings.ts
ApiResponse<T> - Unified error handling

Additional Resources

Project References

Official Documentation

Weekly Installs
2
Repository
cacr92/wereply
GitHub Stars
1
First Seen
5 days ago
Installed on
amp2
cline2
openclaw2
opencode2
cursor2
kimi-cli2