Flutter✖️CloudFunctions開発効率爆上がり!エミュレータ環境の構築

毎回デプロイして確認するのは時間の無駄です。ローカルのエミュレータを使って爆速開発する手順と、その際に必ずぶつかる「App Checkエラー」の回避方法を解説します。

1. Cloud Functions側の実装 (App Check回避)

エミュレータからのリクエストには正規のApp Checkトークンがないため、そのままではエラーになります。
「本番環境(FUNCTIONS_EMULATORfalse)の時だけチェックする」 という条件分岐を入れます。

functions/src/index.ts

import * as functions from "firebase-functions/v1";

// 日本リージョンを指定(任意)
export const helloWorld = functions
  .region('asia-northeast1')
  .https.onCall((data, context) => {

    // ★ 開発環境かどうかの判定
    const isEmulator = process.env.FUNCTIONS_EMULATOR === "true";

    // 「エミュレータじゃない(本番)」かつ「App Check認証がない」場合のみエラーにする
    if (!isEmulator && context.app == undefined) {
      throw new functions.https.HttpsError(
        'failed-precondition',
        'The function must be called from an App Check verified app.'
      );
    }

    functions.logger.info("🎉 ガードを突破!処理を開始します。");

    return { message: "成功!これはセキュアな通信(またはエミュレータ)です。" };
});

2. Flutter側の実装 (エミュレータ接続)

アプリが本番サーバーではなく、あなたのパソコン(ローカル)を見に行くように設定します。

lib/main.dart

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  // ★ 開発モード(Debug)の時だけエミュレータを使う
  if (kDebugMode) {
    // Androidエミュレータは '10.0.2.2'、iOSは 'localhost' を使うのが定石
    final host = Platform.isAndroid ? '10.0.2.2' : 'localhost';

    try {
      // リージョンを指定している場合は instanceFor(region: ...) が必須!
      FirebaseFunctions.instanceFor(region: 'asia-northeast1')
          .useFunctionsEmulator(host, 5001);

      print('🚀 Functions connected to emulator: $host:5001');
    } catch (e) {
      print(e);
    }
  }

  runApp(const MyApp());
}

3. エミュレータの起動とコンパイル(最重要!)

TypeScriptを使っている場合、「保存しただけでは反映されない」 という罠があります。必ずJSへの変換(コンパイル)が必要です。

ターミナルA(コンパイル監視用)

cd functions
npm run build:watch
# これで .ts を保存するたびに自動で .js に変換されます

ターミナルB(エミュレータ起動用)

firebase emulators:start
# ログはここに流れます

4. 動作確認 (curlコマンドの作法)

アプリを作る前に、ターミナルから curl で疎通確認をするのがおすすめです。
ただし、onCall 関数は特殊なルール(data キーで包む)があるため注意してください。

# 成功するコマンド例
curl -X POST \
  -H "Content-Type: application/json" \
  -d '{"data": {"text": "テスト"}}' \
  http://127.0.0.1:5001/[プロジェクトID]/asia-northeast1/helloWorld
  • ポイント1: Content-Type: application/json は必須。
  • ポイント2: 送信データは必ず {"data": ...} で包むこと!

💡 トラブルシューティング

Q: FAILED_PRECONDITION エラーが消えない!
A: Functionsのコードが古いまま動いている可能性大です。npm run build を手動で実行するか、watch が動いているか確認してください。

Q: 404 Not Found になる!
A: URLのプロジェクトID、関数名、そしてリージョンが合っているか確認してください。特にFlutter側で instanceFor(region: ‘asia-northeast1’) を忘れると、デフォルトの us-central1 を探しに行ってしまい404になります。

Q: ログが見にくい!
A: ブラウザで http://localhost:4000/logs を開くと、綺麗に整形されたログが見られます

コメント

タイトルとURLをコピーしました