# 【Flutter】FCMとSupabase Edge FunctionsでPush通知〜第3回〜

> Flutterを使用してSupabase Edge Functionsを介してPush通知を送信する方法を解説。Firebaseの設定からSupabaseのインストール、Edge Functionsの実装、デプロイまでの流れを紹介します。

- 公開日: 2024-12-11
- 著者: Matsu
- タグ: Flutter, Supabase
- URL: https://tech.anycloud.co.jp/articles/push-notification-flutter-supabase-3

---

前回に続き、今回はSupabaseからのpush通知送信まで進めていきます。

<div class="link-card-wrap"><a class="link-card" href="/articles/push-notification-flutter-supabase-2/" target="_blank" rel="noopener noreferrer"><span class="link-card-body"><span class="link-card-title">【Flutter】FCMとSupabase Edge FunctionsでPush通知〜第2回〜</span><span class="link-card-description">Flutterを用いたFCM（Firebase Cloud Messaging）とSupabase Edge FunctionsによるPush通知の実装方法について解説。具体的に通知の許可ダイアログ実装、バックグラウンド・フォアグラウンド状態でのPush通知の受信方法、Firebase Messagingでのテスト送信手順を紹介。</span><span class="link-card-meta"><img class="link-card-favicon" src="./linkcard-01-favicon.ico" alt=""><span class="link-card-domain">tech.anycloud.co.jp</span></span></span><img class="link-card-image" src="./linkcard-01-image.webp" alt=""></a></div>

## 改めて開発の流れの紹介

1.  Firebaseの設定
2.  Firebaseの環境分け（devとprod）
3.  FlutterでFirebase初期化を実装
4.  FCMの設定【← 第1回はここまで】
5.  Flutterで通知許可ダイアログを実装
6.  バックグラウンド・フォアグラウンド・ターミネート状態でのPush通知の受信
7.  Firebase Messagingでテスト送信【← 第2回はここまで】
8.  Supabaseインストール
9.  Supabase Edge Functionsの実装
10.  Webhookを設定
11.  SupabaseからFlutterアプリにPush通知を送信【← 第3回はここまで】

## Supabaseインストール

Supabase Edge Functionsを作成するにあたり、Supabaseのインストールからしておきます。

<div class="link-card-wrap"><a class="link-card" href="https://supabase.com/docs/guides/functions/examples/push-notifications" target="_blank" rel="noopener noreferrer"><span class="link-card-body"><span class="link-card-title">Sending Push Notifications | Supabase Docs</span><span class="link-card-description">Send Push Notifications to your React Native iOS and Android apps using Expo.</span><span class="link-card-meta"><img class="link-card-favicon" src="./linkcard-02-favicon.ico" alt=""><span class="link-card-domain">supabase.com</span></span></span><img class="link-card-image" src="./linkcard-02-image.webp" alt=""></a></div>

```shell
% brew install supabase/tap/supabase
...
==> Running `brew cleanup supabase`...
Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).
```

## Supabase Edge Functionsの土台を作成

```shell
% supabase init

Generated VS Code settings in .vscode/settings.json. Please install the recommended extension!
Finished supabase init.
```

```shell
% supabase functions new push_notification
```

### Denoの環境設定

`名前 'Deno' が見つかりません。`とエラーが出ているので、`new push`で生成されたコードのガイドに従ってDenoの環境設定を行います。

<div class="link-card-wrap"><a class="link-card" href="https://docs.deno.com/runtime/getting_started/setup_your_environment/" target="_blank" rel="noopener noreferrer"><span class="link-card-body"><span class="link-card-title">Set up your environment</span><span class="link-card-description">A guide to setting up your development environment for Deno. Learn how to configure popular editors like VS Code, set up language server support, and enable shell completions for better productivity.</span><span class="link-card-meta"><img class="link-card-favicon" src="./linkcard-03-favicon.ico" alt=""><span class="link-card-domain">docs.deno.com</span></span></span><img class="link-card-image" src="./linkcard-03-image.webp" alt=""></a></div>

```shell
Follow this setup guide to integrate the Deno language server with your editor:
<https://deno.land/manual/getting_started/setup_your_environment>
This enables autocomplete, go to definition, etc.

---
【翻訳】
Deno 言語サーバーをエディターに統合するには、このセットアップ ガイドに従ってください:
<https://deno.land/manual/getting_started/setup_your_environment>
これにより、オートコンプリート、定義への移動などが有効になります。
```

1.  VSCodeで「[Deno](https://marketplace.visualstudio.com/items?itemName=denoland.vscode-deno)」の拡張機能を追加
2.  `Ctrl+Shift+P` のコマンドパレットから`Deno: Initialize Workspace Configuration` を実行
3.  ワークスペース用に Deno を構成

### devとprodで環境分け

firebaseからdevとprodで秘密鍵の生成をしておきます。

<figure><img src="./image-001.webp" alt="devとprodで環境分け" width="2176" height="1504"></figure>

## Supabase Edge Functionsの実装

Supabase Edge Functionsの実装の一部分だけを紹介します。

DB構成によって変わるので、ご自身の環境に合わせてコードを変更してください。

`supabase/functions/push_notification/index.ts`

```typescript
import { createClient } from "npm:@supabase/supabase-js@2";
import { JWT } from "npm:google-auth-library@9";
import { config } from "<https://deno.land/std@0.138.0/dotenv/mod.ts>";

await config({ export: true });

interface NotificationManagement {
  id: string;
  title: string;
  body: string;
  created_at: Date;
  updated_at: Date;
}

interface WebhookPayload {
  type: "INSERT";
  table: string;
  record: NotificationManagement;
  schema: "public";
  old_record: null | NotificationManagement;
}

const supabase = createClient(
  Deno.env.get("SUPABASE_URL")!,
  Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!
);

Deno.serve(async (req) => {
  const payload: WebhookPayload = await req.json();

  const { title, body } = payload.record;

  const firebaseClientEmail = Deno.env.get("FIREBASE_CLIENT_EMAIL");
  const firebasePrivateKey = Deno.env
    .get("FIREBASE_PRIVATE_KEY")!
    .replace(/\\\\n/g, "\\n");
  const firebaseProjectId = Deno.env.get("FIREBASE_PROJECT_ID");

  const accessToken = await getAccessToken({
    clientEmail: firebaseClientEmail,
    privateKey: firebasePrivateKey,
  });

  for (const account of accounts) {
    const fcmToken = account.account_fcm_token?.fcm_token;
    if (fcmToken) {
      await sendPushNotification(
        fcmToken,
        title,
        body,
        accessToken,
        firebaseProjectId
      );
    }
  }

  return new Response("Push notification sent successfully", { status: 200 });
});

async function sendPushNotification(
  fcmToken: string,
  title: string,
  body: string,
  accessToken: string,
  projectId: string
) {
  const res = await fetch(
    `https://fcm.googleapis.com/v1/projects/${projectId}/messages:send`,
    {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${accessToken}`,
      },
      body: JSON.stringify({
        message: {
          token: fcmToken,
          notification: {
            title,
            body,
          },
        },
      }),
    }
  );

  const resData = await res.json();

  if (res.status < 200 || res.status > 299) {
    console.error(
      `Push notification sending failure: ${JSON.stringify(resData)}`
    );
    throw new Error(
      `Push notification sending failure: ${JSON.stringify(resData)}`
    );
  }
}

const getAccessToken = ({
  clientEmail,
  privateKey,
}: {
  clientEmail: string;
  privateKey: string;
}): Promise<string> => {
  return new Promise((resolve, reject) => {
    const jwtClient = new JWT({
      email: clientEmail,
      key: privateKey,
      scopes: ["<https://www.googleapis.com/auth/firebase.messaging>"],
    });
    jwtClient.authorize((err, tokens) => {
      if (err) {
        reject(err);
        return;
      }
      resolve(tokens!.access_token!);
    });
  });
};
```

ちなみに、firebase`のservice-account.json`あたりの値を`Edge Function Secrets Management`に保管しておきました。

※supabaseの公式youtubeで公開されていたjsonファイルのimportや.envファイルを試しましたが うまくいかず、この方法が確実でした。

## Supabase Edge Functionsのデプロイ

以下コマンドからデプロイします。

```shell
% cd supabase

# dev環境 デプロイ
% supabase functions deploy push_notification --project-ref プロジェクトref（<https://supabase.com/dashboard/project/〇〇〇〇/settings/functionsの〇〇部分）>

# prod環境 デプロイ
% supabase functions deploy push_notification --project-ref プロジェクトref
```

デプロイ成功しました。

<figure><img src="./image-002.webp" alt="Supabase Edge Functionsのデプロイ" width="2574" height="918"></figure>

## Database Webhookを設定する

Supabaseの管理画面からWebhookを作成していきます。

<figure><img src="./image-003.webp" alt="Database Webhookを設定する" width="2344" height="1268"></figure>

<figure><img src="./image-004.webp" alt="Database Webhookを設定する" width="2886" height="1274"></figure>

`Webhook configuration`までは画像の通りですが、それ以下の項目はデフォルトのままで作成しています。

<figure><img src="./image-005.webp" alt="Webhook configuration" width="2902" height="1626"></figure>

## SupabaseからFlutterアプリにPush通知を送信

Functions関数もデプロイして、データベースwebhookの設定もできたので、Push通知を手動でトリガーしてみましょう。

DBからインサート もしくは curlコマンドで試してみるとPush通知が届くようになります。

## まとめ

Supabase自体触ったことなかったのですが、これを機に触ってみて、firebaseよりは使いやすかったなという所感です。

今後、案件でもSupabaseを扱う機会があれば、面白そうです。
