Firebase Cloud FunctionsとFirestoreを使って簡単サーバーレス環境を構築する

## はじめに
Next.js を使って個人開発をしようと思ったので Firebase Cloud Functions と Firestore を使ってサーバーレス環境を構築しました。
備忘録を兼ねた手順書として投稿します。

## Firebase プロジェクトを作成
**1. 初期化コマンドを実行**

```
$ firebase init 
```
<br>


**2. 使用する機能を選択**
→ Functions と Hosting を選択して ```Enter```

```
? Which Firebase CLI features do you want to set up for this folder? Press Space to select features, then Enter to confirm your choices. 
 ◯ Database: Deploy Firebase Realtime Database Rules
 ◯ Firestore: Deploy rules and create indexes for Firestore
 ◉ Functions: Configure and deploy Cloud Functions
❯◉ Hosting: Configure and deploy Firebase Hosting sites
 ◯ Storage: Deploy Cloud Storage security rules
 ◯ Emulators: Set up local emulators for Firebase features
```
<br>


**3. 既存の Firebase プロジェクトを使用するか**
→ 今回は既存のプロジェクトを使用する

```
? Please select an option: (Use arrow keys)
❯ Use an existing project 
  Create a new project 
  Add Firebase to an existing Google Cloud Platform project 
  Don't set up a default project 
```
<br>



**4. どのプロジェクトを使用するか**

```
? Please select an option: Use an existing project
? Select a default Firebase project for this directory: (Use arrow keys)
❯ Project ID (Project Name) 
```
<br>


**5. JavaScript と TypeScript どちらの形式で記述するか（今回は TypeScript ）**

```
? What language would you like to use to write Cloud Functions? 
  JavaScript 
❯ TypeScript 
```
<br>


**6. TSLint を使用するか（自由に選択して OK ）**

```
? Do you want to use TSLint to catch probable bugs and enforce style? (Y/n) Y
```
<br>


**7. このタイミングで npm モジュールをインストールしておくか（後でもできる）**

```
? Do you want to install dependencies with npm now? (Y/n) Y
```
<br>


**8. デプロイ対象となるディレクトリを指定する（任意）**

```
? What do you want to use as your public directory? 
```
<br>


**9. SPA かどうか**

```
? Configure as a single-page app (rewrite all urls to /index.html)? (y/N) y
```
<br>


**10. コマンドが終了したらローカルサーバを起動**
→ 表示された URL をコピー

```
$ yarn serve
...
✔  functions[api]: http function initialized (http://localhost:5000/react-firebase/us-central1/api).
```
<br>

**11. 別のターミナルから接続確認**

- コピーした URL + ```/hello```でリクエストを送信  
→ ```Hello Express!``` が返ってくれば成功

```
$ curl http://localhost:5000/react-firebase/us-central1/api/hello
Hello Express!
```
<br>

## Firestoreの 構築

### Firebase Admin SDK の設定

**1. サービスアカウント画面を開く**
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640515/52f26258-4dec-0e82-f783-c8e79292960b.png" width="400px"/>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640515/945aa51a-f086-c7a7-8c2d-00d3c7890e19.png" width="480px"/>
<br>

**2. 秘密鍵を作成する**
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640515/a9215355-7f01-4321-0ec7-f522243d1313.png" width="680px"/>
<br>

**3. ```functions``` ディレクトリ直下に ```model``` ディレクトリを作成し、先程取得した秘密鍵(JSON形式)を配置する**

**4. Admin SDK 構成スニペットをコピーする**
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640515/73e176fb-a7ff-0f48-f668-e2c97014e4f6.png" width="680px"/>
<br>

**5. ```functions/model``` ディレクトリ直下に ```firebase.js``` ファイルを作成し、コピーしたスペニットを貼り付ける**

**6. 貼り付けたスペニットを以下の通りに（2箇所）修正する**

1. ```path/to/serviceAccountKey.json``` を取得した秘密鍵のパスとファイル名に合わせる
2. 一番下の行に ```module.exports = admin;``` を追記する

```ts

var admin = require("firebase-admin");

var serviceAccount = require("path/to/serviceAccountKey.json"); // 1. パスとファイル名を修正

admin.initializeApp({
    credential: admin.credential.cert(serviceAccount),
    databaseURL: "https://test-1-72855.firebaseio.com"
});

module.exports = admin; // 2. module.exports = admin; を追記
```
<br>


### データベースの作成

**1. Firebase コンソールの Database メニューのトップから「データベースの作成」を始める**
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640515/c7475960-c7d4-14c8-278c-8e91d4c25215.png" width="680px"/>
<br>

**2. 今回は「テストモードで開始」を選択する**
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640515/3f647211-bc58-83e2-4368-3232cd5fd0a6.png" width="680px"/>
<br>

**3. ロケーションを選択して完了**
→ asia-northeast1 は ```東京```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640515/df2660f4-0b4f-2a38-98bc-dc247cacdca4.png" width="680px"/>
<br>

### データの準備
**1. コレクションを開始をクリック**
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640515/c1171410-2309-4ef2-aba1-daafd7e8fb36.png" width="680px"/>
<br>

**2. コレクション ID に ```todos``` を入力し次へ**
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640515/e16435c7-3e09-ebcd-2822-4e3e072dc3d5.png" width="680px"/>
<br>

**3.ドキュメント ID、フィールド、タイプ、値を入力して保存**

- ドキュメント ID　：　0001
- フィールド　：　todo
- タイプ　：　string
- 値　：　テスト

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/640515/b08fb310-06fb-211f-8440-1a87e2f950fb.png" width="680px">
<br>

### Functions から Firestore の情報を取得する

**1. Express と cors の導入**

```
$ cd functions
$ yarn add express cors
$ yarn add -D @types/cors
```
<br>


**2. ```functions/index.ts``` を以下の内容に修正する**
→ ```todos``` コレクションの情報を全取得する

```ts
import * as functions from 'firebase-functions';
import * as express from 'express';
import * as cors from 'cors';
// @ts-ignore
import * as admin from '../model/firebase';

const db = admin.firestore();
const app = express();

app.use(cors());
app.get('/', async (req, res, next) => {
    try {
        const todoSnapshot = await db.collection('todos').get();
        const todos = todoSnapshot.docs.map((x: any) => {
            return {
                id: x.id,
                data: x.data()
            };
        });
        res.send(todos);
    } catch (e) {
        next(e);
    }
});

const api = functions.https.onRequest(app);
module.exports = {api};
```
<br>

**3. ローカルサーバを起動**
→ 表示された URL をコピー

```
$ yarn serve
...
✔  functions[api]: http function initialized (http://localhost:5000/react-firebase/us-central1/api).
```
<br>

**4. 別のターミナルから接続確認**

- コピーした URL でリクエストを送信  
→ ```[{"id":"0001","data":{"todo":"テスト"}}]``` が返ってくれば成功！

```
$ curl http://localhost:5000/react-firebase/us-central1/api
[{"id":"0001","data":{"todo":"テスト"}}]
```



## 参考記事
以下の記事を参考にさせて頂きました。

- [taroosg/react-cloudfunctions-firebase-tutorial](https://github.com/taroosg/react-cloudfunctions-firebase-tutorial)
- [Firebase｜【入門】Firestoreの使い方 - わくわくBank](https://www.wakuwakubank.com/posts/721-firebase-firestore-introduction/)