supabaseのtriggerを使ってユーザーのニックネームを自動保存する方法

## はじめに
個人開発を進める際に、supabaseを用いたバックエンド開発に挑戦しました。

その際、emailとpasswordに加えてユーザーのニックネームも保存をしたかったのですが、公式サイトの実装方法が少し古いものだったり、インターネット検索もうまくヒットしなかったりと、かなり時間を費やしたので、備忘録も兼ねて実装内容をまとめようと思います。

これから実装する方の参考になれば幸いです。

## 前提
言語はTypeScript、フレームワークはNext.js、コードエディタはVScodeを使用しています。
ORMにはPrismaを使用して、prisma.schemaに記述したテーブル情報をsupabaseのDatabaseに反映させました。

また、この記事はSupabaseのsignIn()メソッドでユーザーの新規登録機能が実装されている前提で記載しています。未実装の方はユーザー登録実装後に参考にしてください。

## 私が実装したかったこと
個人開発のアプリケーションでは、当初以下のようなテーブル設計を想定していました。
※全てのカラムを書くと長くなるので、必要な箇所だけ抜粋します。

```sql:prisma.schema
model Owner {
  id            String      @id @default(uuid()) @db.Uuid
  nickname      String
  email         String
  password      String
  createdAt     DateTime    @default(now())
  updatedAt     DateTime    @updatedAt

  @@map("owners")
}
```
ownersテーブルに対して、ユーザー認証に必要なemailとpasswordの情報、さらにニックネームを保存する構成です。

しかし、[Supabase Auth](https://supabase.com/docs/guides/auth)機能を使えば、簡単に認証・認可機能を実装できるため、ownersテーブルで全ての情報を管理するのではなく、**「emailとpasswordはauth.usersテーブルに、nicknameはownersテーブルに保存する」出来ないか**と考えました。

#### イメージ図
![0.データベース設計.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1693405/14479ec7-6745-8239-21f6-f6fe032e4f79.png)


## 実装した内容
結論、supabaseの[trigger](https://supabase.com/docs/guides/database/postgres/triggers)を活用することでユーザーの新規登録がされたら、同時に他のテーブルにもデータ保存ができるようになります。

今回実装する内容は、以下の4つです。
1. signUp()メソッドでnicknameの情報を渡す
2. auth.usersテーブルとownersテーブルを外部キーで紐づける
3. ownersテーブルに書き込みするfunctionを作る
4. auth.usersテーブルにレコード追加がされたら、2.のfunctionが実行されるように設定する

### 1. signUp()メソッドでnicknameの情報を渡す
以下のようにsignUp()でoptionを設定します。こうすることで、ユーザー登録をする際にJSON形式で追加情報を送ることができます。

今回はnicknameカラムを設定しました。

```tsx:
const { data, error } = await supabase.auth.signUp({
  email: 'example@email.com',
  password: 'example-password',
  options: {
    data: {
      nickname: 'John',
    },
  },
})
```


### 2. auth.usersテーブルとownersテーブルを外部キーで紐づける
まず、ownersテーブルのidカラムとauth.usersテーブルのidカラムを紐付けします。

ownersテーブルのレコードと、auth.usersテーブルのレコードで同一のuuid情報を保持することで、同一ユーザーと識別させることができます。
![0.データベース設計.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1693405/57c0f290-e4a5-32b3-f00d-2ab76216369a.png)

#### 2-1.Tableを編集する
supabaseのdashboardを開き、サイドバーの「Table Editor」をクリックします。
その後変更したいテーブル名の右横にある「3点リーダー」> 「Edit Table」の順に選択してください。
![1.tableEdit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1693405/700fb1bc-9f5f-9901-00a7-c311d9589808.png)

#### 2-2.idカラムに紐付け設定をする。
「Edit Table」をクリックすると、以下のような画面が表示されます。
真ん中あたりにあるColumnからidカラムを選択し、リンクのアイコンをクリックします。
![2.カラムをリンクさせる.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1693405/ae7f20e9-1b54-c7e1-b77f-1947f6129613.png)

リンクをクリックすると、紐づけるテーブルとカラムを選択できます。
画像と同じように設定してください。
![3.リンクさせるカラムを選択.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1693405/31197d0c-9446-be7a-f491-074a62367186.png)

#### 2-3.オプション設定する
画面下部にある「Action if referenced row removed」で「Cascade」を選択します。
この設定をすることで、auth.usersのレコードが削除された場合は、関連するownersテーブルのデータも一緒に削除することができます。
![4.removeのときにcascadeする.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1693405/924fb901-8063-e5dc-c204-c6919d63a239.png)

最後にSaveボタンを押して保存をしてください。

これでownersテーブルのidカラムに、auth.usersテーブルのidカラムを紐付けすることができました。

### 3. ownersテーブルに書き込みするfunctionを作る
続いて、ownersテーブルにnicknameカラムを書き込みするためのfunction(関数)を作ります。

[こちら](https://supabase.com/docs/guides/database/functions)の動画のように、SQLで作成することもできますが、今回はdashboardから作成します。

#### 3-1. functionを作成する
dashboardのサイドバーにある「Functions」をクリックします。
すると設定画面が表示されるので、schemaで「public」を選択した状態で「Create a new function」をクリックします。
![5.create a new function.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1693405/e5af96e6-17b3-22f0-1bc6-9befd23b2208.png)

以下の画面でfunctionの作成をしていきます。画像のように設定してください。
![6.add new function設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1693405/c8212cf5-1cc5-c3f2-dff9-fe3baeda0153.png)

- Name of function
「関数名」を記載する箇所です。任意で名前を付けられます。

- Return type
関数の戻り値を設定する箇所です。「trigger」を選択します。

- Defenition
実行する関数を記載する箇所です。以下のように記載してください。

```sql
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オブジェクトが含まれていて、メタデータやカスタムデータが格納されています。

```sql:例)raw_user_meta_data
{
	"sub": "uuid形式のデータ",
	"email": "example@email.com",
	"nickname": "John",
	"email_verified": false,
	"phone_verified": false
}
```
なお、このraw_user_meta_dataには、signUp()メソッドで定義したoptionsの`data`オブジェクトが含まれます。

```tsx:例)signUp.tsx
const { data, error } = await supabase.auth.signUp({
  email: 'example@email.com',
  password: 'example-password',
  options: {
    data: {
      nickname: 'John',
    },
  },
})
```
option設定は[こちら](https://supabase.com/docs/guides/auth/managing-user-data#adding-and-retrieving-user-metadata)を合わせて参照してみてください。

#### 3-2. Securityの設定をする
画面下部の「Type of Security」で「SECURITY DEFINEDER」を設定します。
この設定をすることで、この関数を作成したユーザーの権限を使って関数実行が可能になります。
![7.security defenderを設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1693405/24f6896a-6847-6750-7dfa-229728190f2f.png)

### 4.auth.usersテーブルにレコード追加がされたら、2.のfunctionが実行されるように設定する
最後にTriggerを作成します。
Triggerとは特定のイベントと関数を紐付け、イベントの発生時に関数を発火させます。
今回は「auth.usersにレコードが追加された際に、関数実行する」という設定をしていきます。

#### 4-1. triggerの作成をする
dashboardのサイドバーから「SQL Editor」をクリックします。
エディタが開くので、画像のように記述をします。
![8.トリガーの設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1693405/7e2e7c8c-00a0-6c77-4dc2-e259bd83e7ab.png)

```sql:
create trigger トリガー名 // ①任意のトリガー名
after insert on テーブル名 for each row // ②イベント対象になるテーブル。今回はauth.users
execute function 関数名 // ③2.の工程で作成した関数名を記載する
```
上記のように記載をしたら、画面の右下の「Run」ボタンをクリックします。
クリックすることで新規トリガーが作成されます。


#### 4-2. トリガーの作成を確認する
dashboardの「Database」から「Trigger」を選択する。
schemaで「auth」をプルダウンから選択すると、先ほど作成したtriggerが確認できます。
![9.トリガーの確認.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1693405/36715f6f-e44a-3e65-7c8d-8aa8cd67c8a5.png)


## 挙動を確認する
ここまでの設定ができたら、アプリからユーザー登録をしましょう。
以下のようにownersテーブルにもnicknameカラムが保存されていることが確認できればOKです。
![10.ownerテーブルにデータ保存を確認.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1693405/3a317af4-a657-ff2a-687d-ca3b304eafe2.png)


## 最後に
今回は主にdashboardから作成をしましたが、SQL Editorを使ってfunctionやtriggerを作る方法が公式サイトでは紹介されています。

なお、今回公式サイトの動画ではtriggerをdashboardのGUIから作っていましたが、現在その機能がないようです。(ここですごく手こずりました)

なのでSQLから作成することに慣れておく方がベターかもしれません:frowning2: