Skip to content

Utility Process Topologies

Electron’s utility process runs in a separate child process. You can connect it via MessagePort for offloading heavy computation.

// utility-process.ts (runs in a utility process)
import { createParentPortHandler } from 'electron-messageport-trpc/utility';
import { utilityRouter } from './router';
createParentPortHandler({
router: utilityRouter,
parentPort: process.parentPort,
});
// main process
import { createTRPCClient } from '@trpc/client';
import { MessageChannelMain, utilityProcess } from 'electron';
import { mainPortLink } from 'electron-messageport-trpc/main';
const child = utilityProcess.fork('utility-process.js');
const { port1, port2 } = new MessageChannelMain();
child.postMessage({ type: 'connect' }, [port1]);
const client = createTRPCClient({
links: [mainPortLink({ port: port2 })],
});
// main process broker
import { createPortBroker } from 'electron-messageport-trpc/main';
import { utilityProcess } from 'electron';
const child = utilityProcess.fork('utility-process.js');
const broker = createPortBroker();
win.webContents.on('did-finish-load', () => {
const { serverPort } = broker.createRendererPort(win.webContents);
child.postMessage({ type: 'connect' }, [serverPort]);
});

This keeps the main process out of the request path. It only creates and transfers the port pair.

Main Process <== MessagePort ==> Utility Process
Renderer <== MessagePort brokered by Main ==> Utility Process

| Topology | Use Case | Pros | Cons | |---|---|---|---| | Renderer to Main | Standard RPC, DB access, native APIs | Simple setup | Main process is the bottleneck | | Main to Utility | Heavy computation, background tasks | Offloads main thread | Extra process overhead | | Renderer to Utility | Direct offloading via broker | Keeps main out of the request path | Most complex setup |