Shion
【Supabase】サインアップ時に別テーブルにもデータを保存する方法
2024年10月27日
要約を生成中...
はじめに
Supabaseを用いてユーザー認証機能を実装する時、emailやpassword以外の情報も保存したいと思う時ないですか?
例えば、ユーザーのニックネームを保存したり、プロフィール画像を保存したり。
僕のオリジナルアプリの場合は、ユーザーのemailとpasswordに加え、ニックネームを一緒に保存する想定でテーブル設計をしていました。
しかし、公式サイトの情報が古かったり、英語の記事しか出てこなかったりと時間をかなり溶かしたので、今後実装される方の手助けになればと思い、備忘録もかねて実装方法をまとめていきます。
この記事の対象者
- Supabaseでサインアップの実装が完了している方(emailとpasswordで認証ができていればOK)
- 英語が苦手で公式サイトやstackflowで英語読むの疲れたよって方
- サインアップ時に、email,password以外の情報をデータベース保存したい方
前提
- Next.jsでSupabaseでバックエンドを構築していること。
- emailとpasswordを用いてSignUp機能が作成できていること。
実装したこと
まず僕が実装した内容を簡単に説明します。
DB設計当初は、ユーザー情報を保存するためにownersテーブルを作ることを想定していました。
model Owner {
id String @id @default(uuid()) @db.Uuid
nickname String
email String
password String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("owners")
}このOwnerテーブルをSupabaseのauth.usersテーブルに置き換えればいいかーくらいに思っていたのですが、ownersテーブルはnicknameを保存するのみ、あとのemailやpasswordはAuth.users側で管理する実装ができると知って、2つのテーブルに分けて保存する方針にしました。
↓このようなイメージです
実装手順
auth.usersテーブルに対してデータ保存された時に、一緒にownersテーブルに対してもデータ保存するにはトリガーという機能を使います。
トリガーは簡単に言うと、「特定のイベントが実行された時に、関数を発火させるためのスイッチ」みたいなものだと思っていただくとよいかなとおもいます。
auth.usersにレコードが追加された際に、関数実行する」という設定を作っていきます。
実装手順は大きく分けて4工程です。
- signUp()メソッドでnicknameの情報を渡す
- auth.usersテーブルとownersテーブルを外部キーで紐づける
- ownersテーブルに書き込みするfunctionを作る
- auth.usersテーブルにレコード追加がされたら、2.のfunctionが実行されるように設定する
1. signUpメソッドでnicknameの情報を渡す
まずsingUpする時に入力されたemail, password以外の情報を取得する必要があります。
その場合は、SupabaseのsignUpメソッドでoptionを設定します。こうすることで、JSON形式で追加情報を送ることができます。
僕の場合は、以下のようにnicknameカラムを設定しました。
const { data, error } = await supabase.auth.signUp({
email: 'example@email.com',
password: 'example-password',
options: {
data: {
nickname: 'John', // 追加したい項目を記載
},
},
})option設定はこちらも合わせて参照してみてください。
2. auth.usersテーブルとownersテーブルを外部キーで紐づける
次に、ownersテーブルのidカラムとauth.usersテーブルのidカラムを紐付けします。
auth.usersテーブルにもownersテーブルにもユーザー情報が複数登録されていく想定なので、auth.usersテーブルのユーザー情報がどのownersテーブルの情報と紐づくかを管理しておく必要があります。
idカラム同士を紐付けしておくことで、同じidを持つ = 同一ユーザーであると判別できるようになります。
2-1. Tableを編集する
Supabaseのdashboardを開き、サイドバーの「Table Editor」をクリックします。
その後変更したいテーブル名の右横にある「3点リーダー」> 「Edit Table」の順に選択してください。
2-2. idカラムに紐付け設定をする。
「Edit Table」をクリックすると、以下のような画面が表示されます。
真ん中あたりにあるColumnからidカラムを選択し、リンクのアイコンをクリックします。
リンクをクリックすると、紐づけるテーブルとカラムを選択できます。
画像と同じように設定してください。
2-3. オプション設定をする
画面下部にある「Action if referenced row removed」で「Cascade」を選択します。
この設定をすることで、auth.usersのレコードが削除された場合は、関連するownersテーブルのデータも一緒に削除することができます。
最後にSaveボタンを押して保存をしてください。
これでownersテーブルのidカラムに、auth.usersテーブルのidカラムを紐付けが完了です。
3. ownersテーブルに書き込みするfunctionを作る
続いて、ownersテーブルにnicknameカラムを書き込みするためのfunction(関数)を作ります。
こちらの動画のように、SQLで作成することもできますが、今回はdashboardから作成します。
3-1. functionを作成する
dashboardのサイドバーにある「Functions」をクリックします。
すると設定画面が表示されるので、schemaで「public」を選択した状態で「Create a new function」をクリックします。
以下の画面でfunctionの作成をしていきます。画像のように設定してください。
- Name of function
「関数名」を記載する箇所です。任意で名前を付けられます。 - Return type
関数の戻り値を設定する箇所です。「trigger」を選択します。 - Definition
実行する関数を記載する箇所です。以下のように記載してください。
begin
insert into public.テーブル名 (カラム名1, カラム名2) //保存したいテーブル名とカラム名を記載する
values (
new.id,
new.raw_user_meta_data->>'保存したいカラム名',
);
return new;
end;newとは?
valuesの値にnew.idやnew.raw_user_meta_dataの記述があります。
このnewは、新しくデータベースにデータ保存、更新、削除された際の新しいレコードを指します。
つまり、今回はauth.usersのテーブルにデータ保存がされた際に、このfunctionが実行されるためnew.idでauth.usersテーブルのidカラムのデータを取得できます。
また、new.raw_user_meta_dataは、Supabaseのauth.users テーブルで使用されるフィールドの1つです。
このフィールドには、ユーザーに関連する追加情報を保存するためのJSONオブジェクトが含まれていて、メタデータやカスタムデータが格納されています。
{
"sub": "uuid形式のデータ",
"email": "example@email.com",
"nickname": "John",
"email_verified": false,
"phone_verified": false
}なお、このraw_user_meta_dataには、signUpメソッドで定義したoptionsのdataオブジェクトが含まれます。
const { data, error } = await supabase.auth.signUp({
email: 'example@email.com',
password: 'example-password',
options: {
data: {
nickname: 'John',
},
},
})3-2. Securityの設定をする
画面下部の「Type of Security」で「SECURITY DEFINER」を設定します。
この設定をすることで、この関数を作成したユーザーの権限を使って関数実行が可能になります。
4.auth.usersテーブルにレコード追加がされたら、2.のfunctionが実行されるように設定する
最後にトリガーの作成です。トリガー設定はSQL Editから実行しましょう。
4-1. triggerの作成をする
dashboardのサイドバーから「SQL Editor」をクリックします。
エディタが開くので、画像のように記述をします。
create trigger トリガー名 // ①任意のトリガー名
after insert on テーブル名 for each row // ②イベント対象になるテーブル。今回はauth.users
execute function 関数名 // ③2.の工程で作成した関数名を記載する上記のように記載をしたら、画面の右下の「Run」ボタンをクリックします。
クリックすることで新規トリガーが作成されます。
4-2. トリガーの作成を確認する
dashboardの「Database」から「Trigger」を選択する。
schemaで「auth」をプルダウンから選択すると、先ほど作成したtriggerが確認できます。
挙動確認
ここまでの設定ができたら、アプリからユーザー登録をしましょう。
以下のようにauth.usersテーブルに紐づけたテーブルにもデータが保存されていることが確認できればOKです。
注意事項
この実装をする上でハマったエラーを踏まえて、いくつか注意事項があるのでまとめておきます!
1. idカラムはuuid型にしておくこと
最初にエラーにハマったのは、ownersテーブルのPrimary KeyをString型で設定していたことです。
Supabaseはuuidでidカラムが保存されるので、String型で設定しておけばOK!かと思いきや、NGでした。
String型のままだと「2. auth.usersテーブルとownersテーブルを外部キーで紐づける」の行程で、auth.usersテーブルのidカラムとの紐付けができません。(型が違うからだめだよ!と怒られます)
回避するために、schema.prismaでしっかりとidカラムをuuid型で指定しておきましょう。
model Owner {
id String @id @default(uuid()) @db.Uuid ⇦ここ大事。
nickname String
email String
password String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("owners")
}
2. テーブル名に注意する
schema.prismaでmodel名を大文字スタートで記載した場合、そのモデル名がそのままSupabaseのデータベースに反映されてしまいます。
本来テーブル名は、小文字でスネークケース、かつ複数形で記載するのが慣例なので、@@mapを使用してテーブル名を変換しておきましょう。
model Owner {
id String @id @default(uuid()) @db.Uuid
nickname String
email String
password String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("owners") ⇦@@mapを活用して、テーブル名を変換
}
PosgreSQLはテーブル名やカラム名の大文字、小文字を区別せtずに全て小文字として処理します。
そのため、この変換をしておかないと、functionを実行してもテーブル名が見つからず、データ保存ができません。
ここで僕は時間をすごく溶かしたので、テーブル名の命名には注意しておきましょう!
begin
insert into public.Owner (id, nickname) ⇦@@map変換しないと、Ownerをownerに変換してテーブル検索をしてしまう
values (
new.id,
new.raw_user_meta_data->>'nickname',
);
return new;
end;さいごに
長々と書いてしまいましたが、実装手順は上記の通りです。
細かい設定の部分(RLSやポリシーなど)は調べきれていないところが多々ありますが、よかったら参考にしてみてください。

