狐好きぷろぐらまー

狐好きプログラマーのブログです。

【Supabase】対象のEmailの重複確認を行う関数の作成方法とその呼び出し方【Flutter】

こんにちは。pregum_foxです。

最近模様替えを行いモニタの配置が変わって姿勢がよくなりましたが、机の高さを変えられる昇降デスクが欲しくなった今日この頃です。

概要

今回は、supabaseのsignUpメソッドにてデフォルトの挙動ではすでに存在するEmailを渡しても例外を投げないので、ないなら自分で作ってしまおうということで事前に存在チェックをするPostgres functionを定義して、Flutterから呼び出す処理がうまく動作したので記事にしました。

注意点

Issueにはセキュリティの問題からサニタイズされたユーザーデータ(つまりダミーのユーザーデータ)を返すようなので、戻り値で判定できないようになっているので、セキュリティホールにならないように実装が必要です。

以下目次です。

検証環境

内容 項目
Flutter 3.16.4
supabase_flutter 2.1.0

Flutter

fvm flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.16.4, on macOS 14.3 23D56 darwin-arm64, locale ja-JP)
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 15.1)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2023.1)
[✓] VS Code (version 1.86.0)
[✓] Connected device (4 available)
[✓] Network resources

• No issues found!

supabase_flutter(pubspec.lock)

  supabase_flutter:
    dependency: "direct main"
    description:
      name: supabase_flutter
      sha256: "57ca6c970042e6cef29056b0dc71bb17fc9d6848188df5ddc69e819cf28c7923"
      url: "https://pub.dev"
    source: hosted
    version: "2.1.0

Postgres functionsの実装

SQLの説明

それでは実際にEmailの存在判定を行うPostgres functionを実装していきます。

まずはSupabaseの実装したいプロジェクトを開きます。

開いた後、左のメニューから「SQL Editor」を選択します。

するとSQLを記述する画面が表示されると思いますので、そこに実装していきます。

以下のコードを貼り付けてください

-- すでに対象のEmailが登録されていることを確認するFunction

CREATE OR REPLACE FUNCTION check_email(email_address TEXT)
RETURNS int AS $$

DECLARE
    result_count int;
BEGIN
    SELECT count(*) INTO result_count FROM auth.users WHERE email = email_address;
    RETURN result_count;

END
$$ LANGUAGE plpgsql SECURITY DEFINER;

この関数を軽く説明します。

  • CREATE OR REPLACE FUNCTION check_email(email_address TEXT)

email_address: Text を引数に持つcheck_emailという関数を作成、同名の関数が存在時、置き換えます。

  • RETURNS int AS $$

戻り値は、int型で返します。

  • DECLARE result_count int;

result_countという変数をint型で定義します。

  • BEGIN

実行部分の開始を定義するキーワードです。

  • SELECT count(*) INTO result_count FROM auth.users WHERE email = email_address;

ここはよく見るSQLで、auth.usersテーブルを参照して、emailの値がemail_address の値と一致するレコードの個数をresult_count へ代入する

Emailが存在しない時は0、存在する場合は1を返します。

  • RETURN result_count;

result_countを返します。

  • END

実行部分の終了を定義するキーワードです。

  • $$ LANGUAGE plpgsql SECURITY DEFINER;

SECURITY DEFINER は関数定義者の権限でPostgresが実行されるので、いわゆるfirebaseのadminSdkと同様な権限でRLSを無視して実行ができます。

今回はユーザーデータを保持していないユーザーでもチェックをしたいので、DEFINERを使用しました。

上記とは別にSECURITY INVOKER という権限もありこちらは実行したユーザーアカウントの権限でPostgres functionが実行されます。

同名のPostgres functionの確認

SQLを実行する前に既に同名のPostgres functionが存在する場合、上書きされてしまう為、同名のPostgres functionがないか確認します。

左のメニューから「Database」を選択し、次に「Functions」を選択します。

すると以下のような画面が表示されます。

その画面にて、「Name」がcheck_emailになっている関数があれば既に作成されています。

もし上書きして良いのであれば、実行部分の記事まで読み飛ばしてください。

削除したい場合は、右側の3点リーダをクリックすると「Delete function」のメニューがありますのでそこから削除できます。

存在していなければ、まだ作成されていないのでそのままSQLを実行してPostgres functionを作成しても既存のPostgres functionには影響ありません。

SQLの実行

それでは先ほどの「SQL Editor」の画面まで戻りまして、右下の「Run」をクリックします。

成功すると以下のようにResultsタブの下に「Success. No rows returned」というメッセージが表示されます。 失敗時はここにエラーメッセージが表示されるので、そのメッセージを元に修正します。

成功画面

これで、Supabaseの画面上での操作は終了です。

次にFlutterから呼び出す方法を記述します。

Flutterからの呼び出し方

前提として、supabase_flutterプラグインが入っていることが前提です。

入っていない場合は、まずは入れてください。

処理自体は数行で実装できます。

簡単で良いですね 👍

  // =====================
  // ==== 関数の定義部分 ====
  // =====================
  Future<int> checkEmail({required String email}) async {
    final response = await Supabase.instance.client.rpc('check_email', params: {'email_address': email});
    return response;
  }

  // =====================
  // ===== 呼び出し部分 =====
  // =====================

  // 呼び出しはawaitで
  final sameEmailCount = await checkEmail(email: 'test@example.com');
  if (sameEmailCount > 0) {
    // ここで重複時のハンドリングを行う。
    throw const AuthException('このメールアドレスは既に登録されています。');
  }

  // 0件であればそのままsignUp処理を続ける...
        

という感じで、 Flutter側は数行で実装ができます。

私はFirebase authとかと同じようなイメージで、例外を投げてキャッチした側でエラーメッセージを出していたりします!

もう少し全体的なコードが必要な場合は連絡いただけると共有致します。

まとめ

postgres functionとFlutterを組み合わせてEmailのSignUp時に、重複確認をする処理の実装方法を紹介しました。

他にはEdge functionを使うなどの方法もあるかと思いますが、そちらは機会があれば試してみようと思います。

ここまで読んでいただきありがとうございました。

参考記事