Skip to content

Main Process API

import {
mainPortLink,
createWindowMessagePortHandler,
createPortBroker,
createPortHandler,
} from 'electron-messageport-trpc/main';

Creates and maintains MessagePort-backed tRPC handlers for one or more Electron windows.

interface CreateWindowMessagePortHandlerOptions<TRouter extends AnyRouter> {
router: TRouter;
windows: readonly BrowserWindow[];
createContext?: (opts: { window: BrowserWindow }) => Promise<unknown>;
}
ParameterTypeRequiredDescription
routerAnyRouterYesYour tRPC router instance
windowsreadonly BrowserWindow[]YesWindows that should receive a renderer-side port
createContext(opts: { window: BrowserWindow }) => Promise<unknown>NoFactory invoked for each request with the matching window

A WindowMessagePortHandler object.

Stops all managed window handlers and removes their Electron event listeners.

import { createWindowMessagePortHandler } from 'electron-messageport-trpc/main';
import { appRouter } from './router';
app.whenReady().then(() => {
const win = new BrowserWindow({ /* ... */ });
createWindowMessagePortHandler({
router: appRouter,
windows: [win],
createContext: async ({ window }) => ({
windowId: window.id,
}),
});
});

Creates a tRPC client link for MessagePortMain-compatible ports in the main process. Use this when the main process is the tRPC client, such as calling a utility process over MessageChannelMain.

interface MainPortLinkOptions {
port: MessagePortMain | Promise<MessagePortMain>;
}
ParameterTypeRequiredDescription
portMessagePortMain | Promise<MessagePortMain>YesThe client-side main-process port
import { createTRPCClient } from '@trpc/client';
import { MessageChannelMain, utilityProcess } from 'electron';
import { mainPortLink } from 'electron-messageport-trpc/main';
const child = utilityProcess.fork('utility-worker.js');
const { port1, port2 } = new MessageChannelMain();
child.postMessage({ type: 'connect' }, [port1]);
const client = createTRPCClient({
links: [mainPortLink({ port: port2 })],
});

Creates a port broker that generates MessagePort pairs for connecting renderers to the main process.

A PortBroker object.

Creates a MessageChannelMain pair, sends one port to the specified renderer, and returns the other.

Parameters:

ParameterTypeDescription
webContentsWebContentsThe target renderer’s webContents object (e.g., win.webContents)

Returns:

{
serverPort: MessagePortMain;
}

The serverPort is the main-process side of the channel. Pass it to createPortHandler() to start handling tRPC requests.

import { createPortBroker } from 'electron-messageport-trpc/main';
const broker = createPortBroker();
// After the window loads:
const { serverPort } = broker.createRendererPort(win.webContents);

Attaches a tRPC request handler to a MessagePort. It listens for incoming tRPC requests (queries, mutations, subscriptions) and routes them to the given router.

interface CreatePortHandlerOptions<TRouter extends AnyRouter> {
port: MessagePortLike;
router: TRouter;
createContext?: () => Promise<unknown>;
}
ParameterTypeRequiredDescription
portMessagePortMainYesThe server-side MessagePort (from createRendererPort)
routerAnyRouterYesYour tRPC router instance
createContext() => Promise<unknown>NoFactory function called for every request to create the context object

A PortHandler object.

Stops all active subscriptions, aborts their AbortControllers, and closes the underlying MessagePort.

Call this when the associated BrowserWindow is closed or the connection is no longer needed.

import { createPortBroker, createPortHandler } from 'electron-messageport-trpc/main';
import { appRouter } from './router';
const broker = createPortBroker();
app.whenReady().then(() => {
const win = new BrowserWindow({ /* ... */ });
win.webContents.on('did-finish-load', () => {
const { serverPort } = broker.createRendererPort(win.webContents);
const handler = createPortHandler({
port: serverPort,
router: appRouter,
createContext: async () => ({
// per-request context
windowId: win.id,
}),
});
// Clean up when the window closes
win.on('closed', () => {
handler.destroy();
});
});
});

When a subscription request arrives:

  1. The procedure is called and must return an async iterable.
  2. The handler sends { kind: 'result', type: 'started' }.
  3. Each yielded value is sent as a { kind: 'result', type: 'data' } message.
  4. If the procedure yields tracked(id, data), the handler sends the tracked event ID for tRPC v11 resumption.
  5. When the client sends { kind: 'subscription.stop' }, the AbortController is aborted.
  6. When the port closes, all active subscriptions are aborted automatically.