Skip to content

Utility Process Topologies

Electron’s utility process runs in a separate Node.js thread. 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.

Renderer <== MessagePort ==> Main Process <== parentPort ==> Utility Process
TopologyUse CaseProsCons
Renderer to MainStandard RPC, DB access, native APIsSimple setupMain process is the bottleneck
Main to UtilityHeavy computation, background tasksOffloads main threadExtra process overhead
Renderer to UtilityDirect offloading via brokerKeeps main out of the request pathMost complex setup