コンテンツにスキップ

FastAPIをタスクトレイに常駐させる

[質問]

Windowsで、FastAPIを常駐実行し、ローカルHTMLファイルのJavaScriptのfetch()から呼びだせますか。file:// から呼び出す場合、CORS関係の設定が難しいです。

[回答] Gemini

結論から申し上げますと、はい、可能です。 ご指摘の通り、file://(ローカルHTML)から http://localhost(FastAPI)へリクエストを送る場合、ブラウザのセキュリティ制限(CORS)が最大の壁になります。通常、file://Originnull と判定されるため、サーバー側での特別な許可設定が必要です。

これを実現するための具体的な手順と、設定のコツを整理しました。


1. FastAPI側のCORS設定

FastAPIには、CORSを管理するための専用ミドルウェアがあります。file:// からのアクセスを許可するには、allow_originsnull を含めるか、開発用と割り切ってワイルドカード * を使用します。

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

# CORSの設定
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"], # 開発環境なら "*" が一番確実です
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/data")
def read_root():
    return {"message": "Hello from FastAPI!"}

2. Windowsでの常駐実行(バックグラウンド実行)

WindowsでFastAPIを常駐させるには、いくつかの方法がありますが、最も手軽で一般的なのは NSSM (Non-Sucking Service Manager) を使ってWindowsサービスとして登録する方法です。

手順:

  1. NSSMのダウンロード: 公式サイトから入手します。
  2. コマンド実行: 管理者権限のターミナルで以下を入力。 nssm install FastAPI_Service
  3. 設定:
  4. Path: python.exe のパス
  5. Startup directory: プロジェクトのディレクトリ
  6. Arguments: -m uvicorn main:app --host 127.0.0.1 --port 8000

これで、PCを再起動してもバックグラウンドでFastAPIが動き続けます。


3. JavaScriptからの呼び出し

HTMLファイル内のJSからは、通常通り fetch() を使います。

async function fetchData() {
    try {
        const response = await fetch('http://127.0.0.1:8000/data');
        const data = await response.json();
        console.log(data);
    } catch (error) {
        console.error('Error:', error);
    }
}

注意点とヒント

  • Originの挙動: ブラウザによっては file:// からの fetchOrigin ヘッダーが送信されない、あるいは null になることがあります。FastAPI側で allow_origins=["*"] を設定していれば、基本的には回避可能です。
  • ブラウザの制約: 一部の厳格なブラウザ設定では file:// からネットワークへのアクセス自体をブロックする場合があります。もし上記で動かない場合は、VS Codeの拡張機能 Live Server などを使って、HTML側も http://localhost:5500 等でホストすることを検討してください。こちらの方がCORS管理が圧倒的に楽になります。
  • ポートの競合: 常駐させる場合、他のアプリが 8000 ポートを使わないよう注意してください。

まずは FastAPI側で CORSMiddlewareallow_origins=["*"] で設定 して、手動起動した状態で fetch() が通るか試してみるのが近道です。

次に、このFastAPIをWindowsサービス化するための具体的なコマンド例や、NSSMの設定画面の解説が必要でしたらお申し付けください。

★ [続き] タスクトレイ化

[質問]

サービスではなく、通常のアプリとして起動し、タスクトレイにアイコンが表示される形態で作りたいです

[回答] Gemini

Windowsサービスではなく、タスクトレイ(システムトレイ)に常駐する「デスクトップアプリ」のような形態ですね。その場合、Pythonの pystray というライブラリを使ってタスクトレイを制御し、バックグラウンドで uvicorn(FastAPIのサーバー)を別スレッドで動かす構成がベストです。

具体的な実装方法と、配布可能な形式にする手順をまとめました。


1. 必要なライブラリのインストール

まず、タスクトレイ管理用の pystray と、画像処理用の Pillow をインストールします。

pip install pystray Pillow fastapi uvicorn

2. 実装コードの例

以下のコードは、FastAPIを別スレッドで起動しつつ、タスクトレイにアイコンと「終了」メニューを表示するサンプルです。

import threading
import uvicorn
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from pystray import Icon, Menu, MenuItem
from PIL import Image, ImageDraw

# --- 1. FastAPIの設定 ---
app = FastAPI()
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/")
def home():
    return {"status": "running"}

def run_server():
    # 127.0.0.1で起動。ポートは任意
    uvicorn.run(app, host="127.0.0.1", port=8000, log_level="info")

# --- 2. タスクトレイアイコンの設定 ---
def create_image():
    # アイコン画像がない場合、簡易的な円を描画して代用
    image = Image.new('RGB', (64, 64), (255, 255, 255))
    dc = ImageDraw.Draw(image)
    dc.ellipse((10, 10, 54, 54), fill=(0, 122, 204))
    return image

def on_quit(icon, item):
    icon.stop() # トレイアイコンを終了
    # 注: uvicornを完全に止めるにはプロセスごと終了させる処理が別途必要になる場合があります

# アイコンとメニューの定義
icon = Icon("FastAPI_App", create_image(), menu=Menu(
    MenuItem("終了", on_quit)
))

if __name__ == "__main__":
    # FastAPIをサブスレッドで開始
    server_thread = threading.Thread(target=run_server, daemon=True)
    server_thread.start()

    # メインスレッドでタスクトレイアイコンを実行
    icon.run()

3. 実行時のポイント

コンソール画面を消す方法

このまま python main.py で実行すると黒い画面(プロンプト)が残ってしまいます。

  • ファイル拡張子を .py から .pyw に変更して実行すると、コンソールを表示せずにバックグラウンドで起動できます。

実行ファイル化 (exe化)

ユーザーに配布したり、スタートアップに登録しやすくしたりするには、PyInstaller を使います。

pip install pyinstaller
pyinstaller --noconsole --onefile --collect-all uvicorn main.py
  • --noconsole: 黒い画面を出さない設定。
  • --onefile: 1つのexeにまとめる設定。

4. スタートアップへの登録

PC起動時に自動で常駐させたい場合は、作成した exe(または .pyw)のショートカットを作成し、以下のフォルダに配置してください。

shell:startup (Win + R キーで入力して実行すると開くフォルダ)


次のステップへの提案

この方法なら、ユーザーは「アプリを起動した」という感覚でFastAPIを常駐させられます。 「特定のアイコン画像(.pngや.ico)を使って、より本格的な見た目にしたい」 などのご要望はありますか?その場合の画像読み込みコードも提供可能です。

★ [続き2] アイコン画像

[質問]

アイコン画像(.pngや.ico)を使いたいです。

[回答] Gemini

アイコン画像(.ico.png)を読み込んでタスクトレイに表示する方法ですね。Pillow ライブラリを使えば、外部ファイルを簡単に読み込めます。

実行ファイル(.exe)にまとめる場合、「プログラムを実行している場所」と「画像ファイルがある場所」のパス解決が重要になります。


1. アイコン読み込みの実装例

以下のコードでは、実行ファイル化した際にもパスがずれないように get_resource_path 関数を定義しています。

import os
import sys
import threading
import uvicorn
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from pystray import Icon, Menu, MenuItem
from PIL import Image

# --- パス解決用の関数(exe化対策) ---
def get_resource_path(relative_path):
    if hasattr(sys, '_MEIPASS'):
        return os.path.join(sys._MEIPASS, relative_path)
    return os.path.join(os.path.abspath("."), relative_path)

# --- FastAPIの設定 ---
app = FastAPI()
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/")
def home():
    return {"status": "ok"}

def run_server():
    uvicorn.run(app, host="127.0.0.1", port=8000, log_level="info")

# --- タスクトレイの設定 ---
def on_quit(icon, item):
    icon.stop()
    os._exit(0) # プロセスごと終了させる

# アイコン画像の読み込み
# 'app_icon.ico' または 'app_icon.png' を用意してください
icon_path = get_resource_path("app_icon.ico")
image = Image.open(icon_path)

icon = Icon("FastAPI_App", image, title="FastAPI Server", menu=Menu(
    MenuItem("終了", on_quit)
))

if __name__ == "__main__":
    server_thread = threading.Thread(target=run_server, daemon=True)
    server_thread.start()
    icon.run()

2. PyInstaller で画像を含めて exe 化する

アイコン画像(app_icon.ico)をプログラムに同梱して、1つの .exe にまとめるためのコマンドです。

pyinstaller --noconsole --onefile --add-data "app_icon.ico;." --icon=app_icon.ico main.py

オプションの解説:

  • --add-data "app_icon.ico;.": 実行ファイル内部に画像ファイルを埋め込みます(Windowsの場合、区切り文字は ; です)。
  • --icon=app_icon.ico: 生成される .exe ファイル自体のアイコンもこれに設定します。
  • --noconsole: 実行時に黒い画面(コンソール)を出さないようにします。

3. 実装のヒント

  • 画像のサイズ: タスクトレイ用には 32x32 または 64x64 ピクセルの画像が最適です。
  • 透過設定: 背景が透明な .png を使うと、Windowsのタスクバーの色(ダークモード/ライトモード)に馴染んで綺麗に見えます。
  • 右クリックメニュー: MenuItem("設定を開く", open_settings) のように追加すれば、ブラウザで設定画面(http://localhost:8000/docs など)を直接開く機能も作れます。

これで「見た目は普通のWindowsアプリだけど、中身はWeb API」という環境が整います。 「exe化した後に、画像が読み込めない」といったエラーが出た場合のトラブルシューティングなども必要であれば解説します。