# How to Build and Package the OmniRoute Electron Desktop Application

> Learn how to build and package the OmniRoute Electron desktop application. Compile native modules and create installers easily with this guide.

- Repository: [Diego Rodrigues de Sa e Souza/OmniRoute](https://github.com/diegosouzapw/OmniRoute)
- Tags: how-to-guide
- Published: 2026-08-03

---

**Run `npm run electron:build` after `npm run build` to compile native modules and produce platform-specific installers in `./dist/`.**

The OmniRoute Electron desktop application bundles the same routing engine as the web API into a cross-platform desktop client. According to the OmniRoute source code, the build pipeline uses a three-phase approach: preparing a standalone Node runtime, rebuilding native dependencies for Electron's ABI, and packaging with **electron-builder**.

## Prerequisites and Initial Setup

Before building the Electron app, you need to install dependencies and build the web UI bundle.

```bash

# Install all development dependencies

npm ci

# Build the production web UI (required before Electron packaging)

npm run build

```

The web bundle must exist because [`electron/main.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/main.js) serves it through the bundled server in standalone mode.

## The Three-Phase Build Pipeline

### Phase 1: Prepare the Standalone Node Runtime

Electron requires native modules compiled against its specific Node version. The script at `scripts/build/prepare-electron-standalone.mjs` creates a clean `./.build/electron-standalone` tree containing the exact Node version used by Electron.

This isolated runtime ensures that modules like `better-sqlite3` are available in a version-matched environment separate from your development Node installation.

### Phase 2: Rebuild Native Dependencies for Electron

Native addons must be compiled against Electron's ABI, not your system Node. The helper `scripts/build/electronRebuildPlan.mjs` analyzes [`electron/package.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/package.json) and generates the correct `npm rebuild` calls.

This step is automatically invoked by the npm scripts—you don't need to run it manually.

### Phase 3: Package with electron-builder

The final installers are produced by **electron-builder**, configured in the `build` section of [`electron/package.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/package.json). This configuration defines:

- **Output formats**: `nsis` (Windows installer), `dmg` (macOS), `zip`, `AppImage` (Linux)
- **Extra resources**: The compiled runtime from Phase 1
- **File whitelist**: What ships in the final ASAR archive

## npm Scripts for Development and Production

| Script | What It Does |
|--------|--------------|
| `npm run electron:dev` | Launches hot-reloading development instance; watches `src/` and `open-sse/` for changes |
| `npm run electron:build` | Full production pipeline: runtime prep, native rebuild, and electron-builder packaging |
| `npm run electron:release` | Triggers GitHub Actions workflow at [`.github/workflows/electron-release.yml`](https://github.com/diegosouzapw/OmniRoute/blob/main/.github/workflows/electron-release.yml) for signed releases |

The `electron:release` script publishes binaries and generates `latest-*.yml` update manifests consumed by the built-in auto-updater.

## Complete Build Example

```bash

# One-time setup

npm ci
npm run build

# Package the desktop application

npm run electron:build

# Installers appear in ./dist/

#   • Windows: OmniRoute-Setup-*.exe (NSIS)

#   • macOS:   OmniRoute-*.dmg

#   • Linux:   OmniRoute-*.AppImage

```

For iterative UI development with hot-module replacement:

```bash
npm run electron:dev

```

## Key Implementation Files

Understanding these source files helps when customizing the build:

### Main Process Entry Point

[`electron/main.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/main.js) bootstraps the UI, configures the IPC bridge via [`preload.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/preload.js), starts the OmniRoute server in standalone mode, and registers the `autoUpdater` from `electron-updater`.

### Preload Script Security

[`electron/preload.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/preload.js) exposes a safe `electronAPI` to the renderer process, forwarding requests to the bundled server via `fetch`. This isolated context prevents direct Node access from renderer code.

### electron-builder Configuration

The [`electron/package.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/package.json) `build` object controls packaging:

```javascript
// Key configuration concepts from electron/package.json
{
  "build": {
    "extraResources": [
      // Copies ../.build/electron-standalone/node_modules into packaged app
    ],
    "files": [
      // Whitelist of files included in final ASAR
      // CI test suite enforces completeness
    ]
  }
}

```

### SQLite Utilities

[`electron/sqlite-inspection.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/sqlite-inspection.js) verifies local database integrity and handles schema migrations for the desktop client's bundled SQLite database.

## Verification and Testing

The test file [`tests/unit/electron-main.test.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/tests/unit/electron-main.test.ts) enforces packaging correctness. It asserts that every dependency referenced by source code appears in `build.files`, preventing runtime "module not found" errors that only surface in packaged builds.

## Summary

- **Always run `npm run build` before `npm run electron:build`** — the Electron app serves the web bundle
- **Native modules require ABI-specific compilation** — handled automatically by `prepare-electron-standalone.mjs` and `electronRebuildPlan.mjs`
- **Packaging configuration lives in [`electron/package.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/package.json)** — controls installers, extra resources, and ASAR contents
- **Use `electron:dev` for live-reload development** and `electron:release` for CI-driven distribution
- **The auto-updater relies on `latest-*.yml` manifests** published by the GitHub Actions workflow

## Frequently Asked Questions

### What Node version does OmniRoute Electron target?

The build system determines this automatically. The `prepare-electron-standalone.mjs` script extracts Electron's embedded Node version and creates an isolated runtime at `./.build/electron-standalone`. You don't need to manage Node versions manually.

### Why does the build fail with "native module version mismatch"?

This occurs when native modules like `better-sqlite3` are compiled for your system Node instead of Electron's ABI. Run `npm run electron:build` rather than manual rebuilds—it invokes `electronRebuildPlan.mjs` to generate correct compiler flags.

### Can I customize which installer formats are generated?

Yes. Edit the `build` object in [`electron/package.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/package.json). Add or remove targets from the platform-specific configuration. Common options include `nsis` and `zip` for Windows, `dmg` and `zip` for macOS, and `AppImage` or `deb` for Linux.

### How does the auto-updater work in packaged builds?

[`electron/main.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/electron/main.js) registers `autoUpdater` from `electron-updater`. The release workflow publishes version metadata as `latest-*.yml` files. The running client polls these manifests and prompts users when updates are available, downloading and installing them automatically.