GASからFirebase（Firestore）, Supabaseにリクエストする

# はじめに
昨日とあるGoogleChrome拡張機能の開発を依頼されました。
組織単位でデータ投入する際、簡易的にスプレッドシートでマスター情報を書いてもらい、
GASでデータ投入できるようにしたら楽だなぁと思い実装してみました。

# なぜFirestoreとSupabase?
[Supabase](https://supabase.com/)と[Firebase](https://firebase.google.com/?hl=ja)は近頃よく比較に出されますね。特にSupabaseは**The Open Source Alternative**と公式が明言するだけあってFirebaseでできることの大半はSupabaseでできます。
コミュニティも、[タイラーさん（@dshukertjrjp）](https://twitter.com/dshukertjrjp)筆頭に活発であり、すごく期待のできるサービスです。

割とRDBに馴染みのあるエンジニアが多いと思うので、Firebaseより馴染みがあるのではないでしょうか？
とはいえ、Firebaseのドキュメント型のデータ管理も魅力的な部分があるため、筆者はSupabaseを中心としながらFirebaseも有効利用したいですね。

実は拡張機能を作る際、Flutter × Firebaseで開発していましたが、Firebaseのパッケージを入れた途端CSP制約に引っ掛かるようになってしまいうまくビルドできませんでした。
Firebaseはパッケージを導入しないと機能を使えませんが、SupabaseはREST APIも気軽に使えるため、今回はSupabaseを採用しました。
とはいえ、GAS自体はFirebase, Supabaseのどちらからもリクエストの送信ができたため今回はどちらも紹介したいと思います。

# 注意点
! 今回の記事では一部抜粋して要点のみ記載します。
! コードを見やするために拡張子は`.gs`ではなく`.js`としています。
! GASの使い方は割愛致します。
! 一旦最低限リクエストできる状態にしています。GASで管理している以上、ヒューマンエラーに伴うセキュリティの不安はあるため扱う際は運用方針等含めて注意が必要です。

では、実装に行きましょう。

# Firebaseの場合
### 下準備
1. Firebaseプロジェクトを立ち上げましょう。GASだけでなくアプリにも組み込む場合、パッケージやライブラリも導入してください。
1. GASでFirebaseを使うには、プラグインの導入が必須です。以下の記事を参考にしてください
https://qiita.com/PmanRabbit/items/a76b9bc5a9d0239c9f78

### 実装

```firebase_config.js
// FirebaseのAdmin SDKの初期化
const firebaseConfig = {
    "projectId": "projectId",
    "key": "-----BEGIN PRIVATE KEY-----\....==\n-----END PRIVATE KEY-----\n",
    "email": "firebase-adminsdk-dddd@xxx-xxx-xx.iam.gserviceaccount.com",
};
```

```upload_to_firebase.js
// Firestoreの設定
let firestore = FirestoreApp.getFirestore(firebaseConfig.email, firebaseConfig.key, firebaseConfig.projectId);

/// ...  シートから情報を取得 ...

// Firestoreにデータを登録
try {
    firestore.updateDocument(/collectionPath', requestData);
} catch (e) {
    Logger.log(e);
}
```

以上です。Firebaseは簡単でしたね。
Flutter側の制約が無ければ活用したいところでした。

# Supabaseの場合
## 下準備
1. Project Settings > APIから以下の情報を取得
- URL
- service_role
- API Key（anonと書いてあるところ）

### 実装
```supabase_config.js
// Base URL for the Supabase API
const supabaseUrl = "https://projectId.supabase.co";
// API key for the Supabase API
const supabaseKey = "xxxxxxxxx";
// Project ID
const projectId = "xxxxxxxxxx";
// Service Role Key
const serviceRoleKey = "xxxxxxx";
```

```api_rest_cient.js
// GETリクエストを送信する
function getSupabaseData(tableName) {
    const supabaseUrl = "https://" + projectId + ".supabase.co/rest/v1/" + tableName;
    const supabaseApiKey = supabaseKey;
    const serviceRKey = serviceRoleKey;

    // Set up options for the API request
    const options = {
        method: 'GET',
        headers: {
            'apikey': supabaseApiKey,
            'Content-Type': 'application/json',
            'Authorization': 'Bearer ' + serviceRKey,
        },
    };

    // Make the API request and parse the JSON response
    const response = UrlFetchApp.fetch(supabaseUrl, options);
    const data = JSON.parse(response.getContentText());

    // Log the data to the console
    Logger.log(data);
}

// POSTリクエストを送信する
function postSupabaseData(tableName, data) {
    const supabaseUrl = "https://" + projectId + ".supabase.co/rest/v1/" + tableName;
    const supabaseApiKey = supabaseKey;
    const serviceRKey = serviceRoleKey;

    const options = {
        method: 'POST',
        headers: {
            'apikey': supabaseApiKey,
            'Content-Type': 'application/json',
            'Prefer': 'return=representation', // To return the inserted data in the response
            'Authorization': 'Bearer ' + serviceRKey,
        },
        payload: JSON.stringify(data)
    };

    const response = UrlFetchApp.fetch(supabaseUrl, options);
    const result = JSON.parse(response.getContentText());

    Logger.log(result);
}

```

GASでAPIをコールしたい場合、`UrlFetchApp.fetch(リクエストURL, パラメータ等)`でできるのも素晴らしいですね。

```main.js
function main() {
    // 雑にtestテーブルに送信します
    postSupabaseData("test", { "name": "test", "age": 20 });
}
```

以上です。
こうすることでGASからSupabaseにデータ投入することが可能になります。

#### supabaseの注意点
最初、愚直に以下のように実装していました
```sample.js
// POSTリクエストを送信する
function postSupabaseData(tableName, data) {
    const supabaseUrl = "https://" + projectId + ".supabase.co/rest/v1/" + tableName;
    const supabaseApiKey = supabaseKey;

    const options = {
        method: 'POST',
        headers: {
            'apikey': supabaseApiKey,
            'Content-Type': 'application/json',
            'Prefer': 'return=representation'
        },
        payload: JSON.stringify(data)
    };

    const response = UrlFetchApp.fetch(supabaseUrl, options);
    const data = JSON.parse(response.getContentText());

    Logger.log(data);
}
```

実際に動くコードと違う点として、`service role key`を使用していません。
それを使わなければ、SupabaseのRLS制約に引っ掛かるため、HTTPステータスは200なのに
レスポンスが何もないといった状態になります。

この点について、悩んでいたのですがタイラーさんが解決してくれました！
相談に乗っていただきありがとうございます。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">GASでSupabase使うのむずい...<br>url/rest/v1/テーブル名でREST APIできるんかな？ただ、GETで200なのにデータが空ってなんで...</p>&mdash; くろごま (@Kurogoma939) <a href="https://twitter.com/Kurogoma939/status/1641825645901778945?ref_src=twsrc%5Etfw">March 31, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

もしSupabaseに興味を持った方は、タイラーさんと公式のフォローをぜひ！
[タイラーさんのアカウント](https://twitter.com/dshukertjrjp?s=20)
[Supabase公式](https://twitter.com/supabase)

以上です。
最後まで読んでいただきありがとうございました！