IPC API

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

ParameterTypeDescription
commandstringCommand name (must match Tauri backend)
payloadany?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

ParameterTypeDescription
eventstringEvent name to listen for
callbackfunctionFunction 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

ParameterTypeDescription
eventstringEvent name
payloadany?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

ParameterTypeDescription
commandstringCommand 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.