IPC Communication
Type-safe inter-process communication between your frontend and Tauri backend.
Overview
IPC (Inter-Process Communication) allows your frontend JavaScript/TypeScript code to communicate with the Tauri Rust backend. The runtime SDK provides type-safe wrappers around Tauri's IPC system.
Key Concept: Commands are sent from frontend to backend, while events can flow in both directions.
sendCommand
Send a command to the Tauri backend and receive a typed response.
async function sendCommand<T = any>(
command: string,
payload?: any
): Promise<T>Parameters
| Parameter | Type | Description |
|---|---|---|
command | string | Command name (must match Tauri backend) |
payload | any? | Data to send with the command |
Returns
Returns a Promise that resolves with the typed response data.
Example
import { sendCommand } from '@deskforge/runtime';
// Simple command
const result = await sendCommand('greet', { name: 'World' });
console.log(result); // "Hello, World!"
// Typed command
interface User {
id: number;
name: string;
email: string;
}
const user = await sendCommand<User>('get_user', { id: 123 });
console.log(user.name); // TypeScript knows this is a string
// Error handling
try {
const data = await sendCommand('risky_operation');
} catch (error) {
console.error('Command failed:', error.message);
}onEvent
Listen for events emitted from the Tauri backend.
async function onEvent(
event: string,
callback: (payload: any) => void
): Promise<() => void>Parameters
| Parameter | Type | Description |
|---|---|---|
event | string | Event name to listen for |
callback | function | Function to call when event is received |
Returns
Returns an unlisten function to stop listening to the event.
Example
import { onEvent } from '@deskforge/runtime';
// Listen for download progress
const unlisten = await onEvent('download-progress', (payload) => {
console.log(`Downloaded: ${payload.percent}%`);
updateProgressBar(payload.percent);
});
// Stop listening when done
setTimeout(() => {
unlisten();
}, 60000);
// React example
useEffect(() => {
let unlisten: (() => void) | null = null;
onEvent('notification', (payload) => {
showNotification(payload.message);
}).then((fn) => {
unlisten = fn;
});
return () => {
unlisten?.();
};
}, []);emitEvent
Emit an event from the frontend to the Tauri backend.
async function emitEvent(event: string, payload?: any): Promise<void>Parameters
| Parameter | Type | Description |
|---|---|---|
event | string | Event name |
payload | any? | Data to send with the event |
Example
import { emitEvent } from '@deskforge/runtime';
// Emit user action
await emitEvent('user-clicked', {
button: 'submit',
timestamp: Date.now()
});
// Notify backend of state change
await emitEvent('theme-changed', { theme: 'dark' });createIPCHandler
Create a reusable, typed IPC command handler.
function createIPCHandler<T = any>(command: string): (payload?: any) => Promise<T>Parameters
| Parameter | Type | Description |
|---|---|---|
command | string | Command name to create handler for |
Returns
Returns a function that sends the command with optional payload.
Example
import { createIPCHandler } from '@deskforge/runtime';
// Create typed handlers
interface User {
id: number;
name: string;
}
const getUser = createIPCHandler<User>('get_user');
const deleteUser = createIPCHandler<boolean>('delete_user');
const updateSettings = createIPCHandler<void>('update_settings');
// Use the handlers
const user = await getUser({ id: 123 });
console.log(user.name); // Fully typed!
const success = await deleteUser({ id: 123 });
if (success) {
console.log('User deleted');
}
await updateSettings({ theme: 'dark', notifications: true });Complete Example
Here's a complete example showing IPC communication in a real application:
import {
sendCommand,
onEvent,
emitEvent,
createIPCHandler
} from '@deskforge/runtime';
// Define types
interface FileData {
path: string;
content: string;
size: number;
}
// Create typed handlers
const readFile = createIPCHandler<FileData>('read_file');
const writeFile = createIPCHandler<boolean>('write_file');
// Read a file
async function loadFile(path: string) {
try {
const file = await readFile({ path });
console.log(`Loaded ${file.size} bytes from ${file.path}`);
return file.content;
} catch (error) {
console.error('Failed to read file:', error);
return null;
}
}
// Write a file
async function saveFile(path: string, content: string) {
const success = await writeFile({ path, content });
if (success) {
await emitEvent('file-saved', { path });
}
return success;
}
// Listen for file changes from backend
const unlisten = await onEvent('file-changed', async (payload) => {
console.log(`File changed: ${payload.path}`);
const content = await loadFile(payload.path);
updateEditor(content);
});
// Cleanup on unmount
window.addEventListener('beforeunload', () => {
unlisten();
});Best Practices
✓ Use TypeScript
Define interfaces for your command payloads and responses to get full type safety.
✓ Handle Errors
Always wrap IPC calls in try-catch blocks to handle backend errors gracefully.
✓ Cleanup Event Listeners
Call the unlisten function when components unmount to prevent memory leaks.
⚠ Serialize Data
IPC payloads must be JSON-serializable. Avoid sending functions, symbols, or circular references.