Flutter with Supabase!!

# はじめに
こんにちは！もんすんです。
今回は、Flutterでの利用を試しを兼ねて、Supabaseについて紹介していきます。
Supabaseに興味がある方や、バックエンドの構築に手軽さを求めている方は、ぜひご覧ください。

# Supabaseとは

## 概要
Supabaseは、オープンソースの**Backend as a Service (BaaS, バース)**、つまり、バックエンドサービスとして注目されているプラットフォームで、Firebaseの代替としてよく比較されています。

バックエンド機能としては、
- データベース
- 認証
- ストレージ
- サーバーレスファンクション

が備わっています。
これらの機能により、迅速にアプリケーションを構築・展開できる環境を提供しています。

Supabaseでは、**PostgreSQL**が採用されており、リアルタイムデータベースの機能や高度なクエリが可能になっています。
また、RESTful APIやGraphQLを自動生成する仕組みも備えているため、フロントエンド開発者にとっても直感的で扱いやすいのが特徴です。

### 特徴

1. リアルタイムデータベース: 変更が即座に反映されるDBを提供
2. 認証と認可: JWTベースの認証や、OAuth、SAMLなどのログインに対応
3. ストレージ管理: 画像や動画のアップロードおよび管理が可能なストレージ
4. サーバーレスファンクション: カスタムAPIやバックエンド処理を実装可能

## 無料枠
有料プランになってくると他のサービスとの比較が難しくなるので、今回の記事では、無料枠について、FirebaseとAWSとSupabaseを比較してみました。

:::note warn
これは2024年11月時点での情報ですので、正確な情報は各サービスの公式ドキュメントをご確認ください。
:::

以下は、Supabase、Firebase、AWSの無料枠で実現できる主な機能を比較した表です。各プラットフォームの強みや制限も簡潔にまとめています。

| 機能 | Supabase | Firebase | AWS Free Tier |
|:-:|:-:|:-:|:-:|
| **データベース**| PostgreSQL<br>月間500MBまで無料 | Firestore/Realtime Database<br>1GBストレージ | RDS(PostgreSQLやMySQL)<br>750時間/月 無料   |
| **リアルタイム更新** | 対応 | Firestore/Realtime Database対応 | DynamoDB Streams<br>250万回//月まで無料 |
| **認証** | 5万アクティブユーザ/月まで無料 | 5万アクティブユーザ/月まで無料 | Cognito<br>5万アクティブユーザ/月まで無料 |
| **ストレージ**| 画像/ファイルのストレージ<br>1GB無料 | Cloud Storage<br>5GB無料 | S3<br>5GB無料 |
| **サーバーレス関数**  | Edge Functions<br>50万リクエスト/月まで無料 | Cloud Functions<br>200万リクエスト/月まで無料 | Lambda<br>100万リクエスト/月まで無料 |

# 使ってみた

## アカウント作成
こちらからアカウントを作成していきます。

https://supabase.com/dashboard/sign-up

私はGitHub経由でアカウント作成する事にしました。

[Continue with GitHub]を選択し、それ以降は画面の流れにそってプロジェクトを作成して行けばOKです。

![スクリーンショット 2024-11-05 8.25.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1655179/8ec0d364-62bc-652c-66da-6f92abdba892.png)

## Flutterに適用
それでは次にFlutterアプリケーションにSupabaseを適用していきます！
公式ドキュメントは[こちら](https://supabase.com/docs/reference/dart/installing)です。

まず、以下のコマンドを実行し、パッケージをインストールします。
Flutter定番のやつですね。

```shell
flutter pub add supabase_flutter
```

次に`main.dart`に以下の設定を追加します。
これだけで`Supabase`に接続できるみたいです。
本当かよ...

```diff_dart:main.dart
+ import 'package:supabase_flutter/supabase_flutter.dart';

Future<void> main() async {
+  await Supabase.initialize(
+    url: 'https://xyzcompany.supabase.co',
+    anonKey: 'public-anon-key',
+  );
  runApp(const MyApp());
}
```

`url`と`anonKey`には、それぞれプロジェクトダッシュボードで新規プロジェクトを作成する際に提供されるSupabase URLとAPI Keyを入力します。
これらがわからなくなったら、画面左下の歯車:gear:ボタンから、「API」を選択するとひょうじされます。

![スクリーンショット 2024-11-14 9.28.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1655179/5d398d9e-e3b4-2a02-864a-45bbb25f6d92.png)

上記で設定した`Supabase`を呼び出す際は以下のように実装します。

```dart
final supabase = Supabase.instance.client;
```

これで、supabaseが取得できていれば、接続がうまくいっているということになりますね。

## とりあえずDB操作

では、とりあえずDBからデータを取得してみます。

### `Supabase`コンソール側の設定: DB作成

`Supabase`コンソール側に以下のように`test_user`テーブルを作成し、データを追加しました。

テーブルの作成方法やデータの追加方法は、省略します。
作成自体はかなり簡単でした。

![スクリーンショット 2024-11-14 19.24.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1655179/b809b211-0c4a-2b6b-2811-42013a81f32b.png)

### `Supabase`コンソール側の設定: 権限設定
API経由でCRUDを実行するには、以下の権限設定が必要です。

先ほどテーブルの設定を行った画面から「Add RLS policy」を押します。
![スクリーンショット 2024-11-14 19.26.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1655179/20dac5da-b060-89c9-0f78-cbe25a9fdd55.png)

さらに「Create policy」を押します。
![スクリーンショット 2024-11-14 19.28.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1655179/39ce940b-e225-1a83-b3e8-afa6da3594b5.png)

この画面で、`anon`に対して、テーブルの権限を設定します。
今回はCRUDの全ての権限を付与します。
実際に利用する際は正しく権限を与える方がよさそうです。

![スクリーンショット 2024-11-14 19.06.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1655179/73c2b054-296e-a9d0-5a67-2d6df8c0934e.png)


### `Flutter`側の実装

では、Flutter側の実装です。

```dart
import 'package:flutter/material.dart';
import 'package:supabase_flutter/supabase_flutter.dart';

Future<void> main() async {
  await Supabase.initialize(
    url: '',
    anonKey: '',
  );
  final supabase = Supabase.instance.client;
  final data = await supabase.from('test_user').select();

  runApp(MyApp(data));
}

class MyApp extends StatelessWidget {
  final List<Map<String, dynamic>> data;
  const MyApp(this.data, {super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Supabase Flutter Example'),
        ),
        body: ListView(
          children: [
            for (var row in data)
              ListTile(
                title: Text(row['name']),
                subtitle: Text(row['id'].toString()),
              ),
          ],
        ),
      ),
    );
  }
}

```

特に以下の部分が、Readの処理になります。

```dart
final data = await supabase.from('test_user').select();
```

`select`メソッドの引数にカラム名を指定するとそのカラムの値だけ、指定しないと全カラムを取得できます。

### 実行結果
![スクリーンショット 2024-11-14 19.10.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1655179/9808b288-0813-d6a9-1117-e867d6e4296f.png)

正しく`Supabase`のDBからデータを取得することができました！

# 終わりに
Supabaseの基本的な機能と、FirebaseやAWSとの比較、Flutterアプリへの組み込み方法について解説しました。Supabaseは、無料枠で手軽に試すことができるため、モバイルアプリやWebアプリのバックエンドを手軽に始めたい方には大変おすすめです。ぜひ、Supabaseの豊富な機能を活用して、アプリ開発に挑戦してみてもらえたらと思います！