electron-trpc

repository·main·Indexed 19 days ago

https://github.com/jsonnull/electron-trpc

A library for building type-safe Inter-Process Communication (IPC) in Electron applications using tRPC. It bridges the main and renderer processes, allowing developers to expose router-based APIs securely via native Electron IPC instead of a local HTTP server. It supports tRPC Queries, Mutations, and Subscriptions through the use of createIPCHandler in the main process, exposeElectronTRPC in the preload script, and ipcLink in the renderer process.

Tokens
3.7K
Snippets
15
Records
20
Agent score
64%

What's inside electron-trpc

  1. Overview of electron-trpc

    main

    electron-trpc is an ergonomic and type-safe solution for building Inter-Process Communication (IPC) in Electron. It allows you to expose APIs from Electron's main process to one or more renderer processes using the tRPC pattern.

    Key benefits include:

    • Fully type-safe IPC: Leverages tRPC to provide inferred client types, ensuring end-to-end type safety between processes.
    • Secure alternative to localhost: Uses native Electron IPC, which is faster and more secure than running local servers for communication.
    • Full tRPC feature set: Supports Queries, Mutations, and Subscriptions, eliminating the need for manual, complex bi-directional IPC schemes.
  2. Basic Setup for electron-trpc

    main

    To use electron-trpc, you must configure the main process, the preload script, and the renderer process client. This enables type-safe IPC communication between Electron's main and render processes without opening local servers.

    1. Main Process Configuration

    Use createIPCHandler to attach your tRPC router to specific Electron windows.

    2. Preload Script Configuration

    Use exposeElectronTRPC in your preload script to expose the IPC bridge. Note that electron-trpc requires contextIsolation to be enabled (which is the Electron default).

    3. Renderer Process Client

    When initializing your tRPC client in the renderer, use ipcLink from electron-trpc/renderer instead of standard HTTP links.

    // 1. Main Process
    import { app, BrowserWindow } from 'electron';
    import { createIPCHandler } from 'electron-trpc/main';
    import { router } from './api';
    
    app.on('ready', () => {
      const win = new BrowserWindow({
        webPreferences: {
          preload: 'path/to/preload.js',
        },
      });
    
      createIPCHandler({ router, windows: [win] });
    });
    
    // 2. Preload Script
    import { exposeElectronTRPC } from 'electron-trpc/main';
    
    process.once('loaded', async () => {
      exposeElectronTRPC();
    });
    
    // 3. Renderer Process
    import { createTRPCProxyClient } from '@trpc/client';
    import { ipcLink } from 'electron-trpc/renderer';
    
    export const client = createTRPCProxyClient({
      links: [ipcLink()],
    });
  3. Configure the Preload script

    main

    Because electron-trpc relies on Electron's Context Isolation, you must use a preload script to expose the electron-trpc IPC channel to the renderer process. Use exposeElectronTRPC from electron-trpc/main within the process.once('loaded', ...) lifecycle hook.

    import { exposeElectronTRPC } from 'electron-trpc/main';
    
    process.once('loaded', async () => {
      exposeElectronTRPC();
    });
  4. Initialize the tRPC client in the Renderer process

    main

    To communicate with the main process, create a tRPC proxy client in your renderer code using createTRPCProxyClient from @trpc/client. You must provide the ipcLink from electron-trpc/renderer in the links array. This replaces standard HTTP or WebSocket links.

    import { createTRPCProxyClient } from '@trpc/client';
    import { ipcLink } from 'electron-trpc/renderer';
    
    export const client = createTRPCProxyClient({
      links: [ipcLink()],
    });
  5. Set up the tRPC IPC handler in the Main process

    main

    In your Electron main process, use createIPCHandler to connect your tRPC router to specific windows. You must pass your router and an array of windows (the BrowserWindow instances) to the handler. Ensure the windows are configured to use the preload script created in the previous step.

    import { app } from 'electron';
    import { createIPCHandler } from 'electron-trpc/main';
    import { router } from './api';
    
    app.on('ready', () => {
      const win = new BrowserWindow({
        webPreferences: {
          // Replace this path with the path to your BUILT preload file
          preload: 'path/to/preload.js',
        },
      });
    
      createIPCHandler({ router, windows: [win] });
    });
  6. Initialize the tRPC IPC handler with createIPCHandler

    main

    In the Electron main process, use createIPCHandler to register your tRPC router and bind it to your application windows. This enables the renderer process to communicate with the tRPC router via IPC.

    When initializing, you can optionally provide a createContext function to inject context into your tRPC procedures and an array of windows to automatically attach the handler to specific BrowserWindow instances.

    import { createIPCHandler } from 'electron-trpc';
    import { app, BrowserWindow } from 'electron';
    import { router } from './router'; // Your tRPC router
    
    const createWindow = () => {
      const win = new BrowserWindow({ /* ... */ });
      
      // Initialize the handler
      createIPCHandler({
        router,
        windows: [win],
        createContext: async (opts) => {
          // Return your context here
          return { /* ... */ };
        },
      });
    
      win.loadURL('...');
    };
    
    app.whenReady().then(createWindow);
  7. Manage window attachment with IPCHandler

    main

    The IPCHandler class (returned by createIPCHandler) allows you to manually manage which windows are connected to the tRPC bridge.

    • attachWindow(win: BrowserWindow): Adds a new window to the handler. This is useful if windows are created dynamically after the handler has been initialized.
    • detachWindow(win: BrowserWindow, webContentsId?: number): Removes a window from the handler and cleans up any active subscriptions associated with that window or its web contents.

    Note: If you call detachWindow on a window that has already been destroyed, you must provide the webContentsId as an argument to avoid errors.

    const handler = createIPCHandler({ router });
    
    // Later, when a new window is created:
    const newWin = new BrowserWindow();
    handler.attachWindow(newWin);
    
    // When a window is being closed or detached:
    handler.detachWindow(newWin);
  8. Expose tRPC to the Electron Renderer via exposeElectronTRPC

    main

    Use exposeElectronTRPC to bridge your tRPC router from the Electron Main process to the Renderer process. This function sets up the necessary IPC communication so that your frontend can call procedures defined in your backend router.

    import { exposeElectronTRPC } from 'electron-trpc';
    import { app, BrowserWindow } from 'electron';
    import { yourRouter } from './router';
    
    // Inside your main process setup
    exposeElectronTRPC(app, yourRouter);