メインコンテンツまでスキップ

Electronではじめる

このガイドでは、ElectronアプリにZeltアプリケーションを組み込む手順を説明します。

前提条件

  • Node.js v20以上(またはBun v1.0以上)
  • パッケージマネージャ: pnpm(推奨)、npm、またはbun

Electronでは追加で以下が必要です:

  • Electron — 最小バージョンは @zeltjs/adapter-electron のpeer dependencyを参照してください
  • electron-vite(推奨)

インストール

pnpm add @zeltjs/core @zeltjs/adapter-electron
pnpm add -D electron electron-vite

アーキテクチャ概要

サーバーベースのadapter(Node.js、Bun)とは異なり、Electron adapterはHTTPソケットの代わりにIPCで通信します。あなたのZeltアプリはmain processで動作し、rendererはIPCブリッジを通じてそれを呼び出します:

必要なのは3つの要素です:

  1. Main processonElectron(app) がZeltを起動し、IPCハンドラを登録します
  2. PreloadスクリプトexposeIpc() がIPCをrendererへブリッジします
  3. Renderer processipcFetch() がブリッジ経由でリクエストを送信します

プロジェクト構成

my-electron-app/
├── src/
│   ├── main/
│   │   ├── entry/
│   │   │   └── hello.controller.ts
│   │   ├── app.ts           # Zeltアプリ定義
│   │   └── index.ts         # Electron mainエントリ
│   ├── preload/
│   │   └── index.ts         # IPCブリッジのセットアップ
│   └── renderer/
│       └── src/
│           └── api/
│               └── zeltFetch.ts  # IPC fetchラッパー
├── electron.vite.config.ts
├── package.json
└── tsconfig.json

Hello World

Step 1: Controllerを作成する

src/main/entry/hello.controller.ts を作成します:

@Controller('/hello')
export class HelloController {
  @Get('/:name')
  greet(req = request()) {
    const name = req.pathParam('name');
    return { message: `Hello, ${name}!` };
  }
}

Step 2: アプリケーションを作成する

src/main/app.ts を作成します:

export const app = createApp([http({
    controllers: [HelloController],
  })]);

Step 3: Main Processで初期化する

src/main/index.ts を作成します:

const bootstrap = async () => {
  const electronZelt = await onElectron(app, {
    ipcChannel: 'http://zelt-app',
  });

  const win = new BrowserWindow({
    width: 900,
    height: 670,
    webPreferences: {
      preload: join(__dirname, '../preload/index.js'),
      contextIsolation: true,
      sandbox: true,
    },
  });
  win.loadFile(join(__dirname, '../renderer/index.html'));
};

void bootstrap();

onElectron() はZeltランタイムを起動し、指定されたchannelのIPCハンドラを自動的に登録します。返されるのは:

  • http.fetch(request)Request を処理して Response を返します(設定された各featureはそのkeyでnamespace化されます。デフォルトは http)
  • shutdown() — アプリケーションをgracefulにシャットダウンします
  • get<T>(Class) — DIコンテナからserviceを解決します
important

webPreferences.preload のパスは、コンパイル済みのpreloadスクリプトを指している必要があります。指定しないと exposeIpc() が実行されず、rendererはZeltアプリに到達できません。

Step 4: Preloadスクリプトをセットアップする

src/preload/index.ts を作成します:

exposeIpc({ channel: 'http://zelt-app' });

exposeIpc() はElectronの contextBridge を使って、IPC senderをrendererへ安全に公開します。

Step 5: RendererからAPIを呼び出す

src/renderer/src/api/zeltFetch.ts を作成します:

export const zeltFetch = (input: RequestInfo | URL, init?: RequestInit): Promise<Response> =>
  ipcFetch(input, init, { channel: 'http://zelt-app' });

標準の fetch() と同じように使います:

const response = await zeltFetch('http://zelt-app/hello/world');
const data = await response.json();
console.log(data.message); // "Hello, world!"
important

channel文字列(例: 'http://zelt-app')は、main・preload・rendererの3層すべてで一致している必要があります。

次のステップ