コピペで使うFirebase【Local Emulator Suite編】

# 概要
Firebase Local Emulator Suiteを使う際にコピペで動かしたいのでそのまとめです。

**公式ドキュメント**
[Firebase Local Emulator Suite の概要](https://firebase.google.com/docs/emulator-suite?hl=ja)
[サーバーに Firebase Admin SDK を追加する](https://firebase.google.com/docs/admin/setup?hl=ja)
[アプリを接続してプロトタイピングを開始する](https://firebase.google.com/docs/emulator-suite/connect_and_prototype?hl=ja)
[Firebase を JavaScript プロジェクトに追加する](https://firebase.google.com/docs/web/setup?hl=ja)


- [セットアップ](#セットアップ)
  - [ローカルで設定する場合](#ローカルで設定する場合)
     - [Firebase CLIのインストール](#firebase-cliのインストール)
     - [プロジェクト設定](#プロジェクト設定)
     - [ローカルで起動](#ローカルで起動)
  - [Docker-composeで設定する場合](#docker-composeで設定する場合)
     - [コンテナで起動](#コンテナで起動)
- [エミュレーターへ接続](#エミュレーターへ接続)
    - [firebase SDK](#firebase-sdk)
    - [Android/iOS](#androidios)
    - [Admin SDK](#admin-sdk)


# セットアップ

## ローカルで設定する場合
インストールする前に、以下が必要です。

- Node.js バージョン 8.0 以降。
- Java バージョン 1.8 以降。

### Firebase CLIのインストール
以下を参考
[Firebase CLI リファレンス](https://firebase.google.com/docs/cli?hl=ja#install_the_firebase_cli)

※Node.js v10.13.0 以降が必要です。

```bash
$ npm install -g firebase-tools
```

### プロジェクト設定
```bash
# Firebase にログイン
$ firebase login

# 初期化
$ firebase init

# エミュレーターの設定
$ firebase init emulators
```

### ローカルで起動

`--import=<パス>`でfirestoreのデータを読み込んで起動する
`--export-on-exit=<パス>`で追加したデータをエクスポートする

[起動時のコマンド(オプション)](https://firebase.google.com/docs/emulator-suite/install_and_configure?hl=ja#startup)

```bash
$ docker compose build && docker compose up -d

$ docker compose exec firebase bash

# 指示に従いログイン
$ firebase login --no-localhost

# 必要であれば
$ firebase use <project_id>

$ firebase emulators:start [--option]

# 例
$ firebase emulators:start --import=data/firestore --export-on-exit
```


## Docker-composeで設定する場合

ディレクトリー構成などは以下を参考にしてください
https://github.com/koffe0522/docker-templates/tree/main/firebase

```Dockerfile:Dockerfile
FROM node:22-alpine

RUN apk update \
 && apk --no-cache add openjdk11-jre-headless \
 && rm -rf /var/cache/apk/*
RUN npm install -g firebase-tools 
RUN mkdir functions

COPY ./package*.json ./
COPY ./functions/package*.json ./functions/.
```

```docker-compose.yml
version: "3"

networks:
  backend:

services:
  firebase:
    build: .
    container_name: firebase
    networks:
      - backend
    ports:
      - 9099:9099 # Auth
      - 4000:4000 # Emulator Suite UI
      - 5001:5001 # Cloud Functions
      - 8080:8080 # Cloud Firestore
      - 9000:9000 # Realtime Database
      - 5000:5000 # Hosting
      - 8085:8085 # Pubsub
    volumes:
      - ./:/opt/workspace:cached
    working_dir: /opt/workspace
    tty: true
    command: firebase emulators:start --import=data/firestore --export-on-exit
```

※以下は`firebase init`で生成される

```.firebaserc:.firebaserc
{
  "projects": {
    "default": <project_id>
  }
}
```

```firebase.json
{
  "database": {
    "rules": "rules/database.rules.json"
  },
  "firestore": {
    "rules": "rules/firestore.rules"
  },
  "functions": {
    "predeploy": [
      "npm --prefix \"$RESOURCE_DIR\" run lint",
      "npm --prefix \"$RESOURCE_DIR\" run build"
    ]
  },
  "hosting": {
    "public": "public",
    "ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  },
  "storage": {
    "rules": "rules/storage.rules"
  },
  "emulators": {
    "auth": {
      "port": 9099,
      "host": "0.0.0.0"
    },
    "functions": {
      "port": 5001,
      "host": "0.0.0.0"
    },
    "firestore": {
      "port": 8080,
      "host": "0.0.0.0"
    },
    "database": {
      "port": 9000,
      "host": "0.0.0.0"
    },
    "hosting": {
      "port": 5000,
      "host": "0.0.0.0"
    },
    "pubsub": {
      "port": 8085,
      "host": "0.0.0.0"
    },
    "ui": {
      "enabled": true,
      "host": "0.0.0.0",
      "port": "4000"
    }
  }
}
```

# エミュレーターへ接続

## firebase SDK
SPAなどクライアントからエミュレーターにアクセスする場合

### インストール
```bash
$ npm install firebase --save
```

### 設定
以下はサンプル
エミュレーターを使用するもののみ設定

```js:SDK V9
import * as firebase from "firebase/app";
import { getAnalytics } from "firebase/analytics";
import { getAuth, connectAuthEmulator } from "firebase/auth";
import { getFunctions, connectFunctionsEmulator } from "firebase/functions";
import { getFirestore, connectFirestoreEmulator } from "firebase/firestore";

const firebaseConfig = {
    /* firebase config */
}

// 初期化は一度だけ
if (!firebase.getApps().length) {
  firebase.initializeApp(firebaseConfig);
}
getAnalytics()

// emulator設定
// ※本番などでは使用しないように注意する
connectAuthEmulator(getAuth(), "http://localhost:9099");
connectFunctionsEmulator(getFunctions(), "localhost", 5001);
connectFirestoreEmulator(getFirestore(), "localhost", 8080);
```

```js:SDK ~V8
import firebase from "firebase/app"
import "firebase/analytics"
import "firebase/auth"
import "firebase/functions"
import "firebase/firestore"

const firebaseConfig = {
    /* firebase config */
}

// 初期化は一度だけ
if (!firebase.apps.length) {
    firebase.initializeApp(firebaseConfig);
}
firebase.analytics()

// emulator設定
// ※本番などでは使用しないように注意する
firebase.auth().useEmulator("http://localhost:9099");
firebase.functions().useEmulator("localhost", 5001);
firebase.firestore().useEmulator("localhost", 8080);
```

#### ローカルホストで切り分ける場合

```js
if (['localhost', /** 起動するホスト */].includes(location.hostname)) {
   // emulator設定
}
```

## Android/iOS

- [Authenticationに接続する](https://firebase.google.com/docs/emulator-suite/connect_auth?hl=ja#android)
- [Cloud Firestoreに接続する](https://firebase.google.com/docs/emulator-suite/connect_firestore?hl=ja#android_ios_and_web_sdks)
- [Cloud Functionsに接続する](https://firebase.google.com/docs/emulator-suite/connect_functions?hl=ja#instrument_your_app_for_callable_functions)

## Admin SDK

### インストール
```bash
$ npm install firebase-admin --save
```

### 設定
以下はサンプル
エミュレーターを使用するもののみ設定

```js
import admin from 'firebase-admin';

// emulator設定
// ※本番などでは使用しないように注意する
process.env.FIREBASE_AUTH_EMULATOR_HOST = "localhost:9099";
process.env.FIRESTORE_EMULATOR_HOST = "localhost:8080";

// 初期化は一度だけ
if (!firebase.apps.length) {
    firebase.initializeApp({
        credential: admin.credential.cert(/** path */)
    });
}

```

#### 環境変数で設定する場合

```bash
$ export FIRESTORE_EMULATOR_HOST="localhost:8080"
$ export FIREBASE_AUTH_EMULATOR_HOST="localhost:9099"
```