Supabaseでログインユーザーを削除する際に画像も一緒に削除する

Supabase と Next.js を使用して、ログインユーザーを削除する際にStorageに保存してある画像も一緒に削除する方法を紹介します。
SupabaseではAuthのUserを削除する場合は、公式のドキュメントを見る限りservice_role_keyをつかってサーバーサイドで実行する必要があります。

https://supabase.com/docs/reference/javascript/auth-admin-deleteuser

ログインユーザーを削除する方法

service_role_keyは、PostgreSQLデータベースに対するフルアクセス権を持つ秘密鍵です。この鍵は、Supabaseプロジェクトの設定から取得できます。

.envにSERVICE_ROLE_KEYを定義します。
SERVICE_ROLE_KEYはサーバーサイドでのみアクセス可能としたいのでNEXT_PUBLIC_はつけないようにします。

SERVICE_ROLE_KEY=hogehoge

pages/apiディレクトリにAuth.userを削除するためのコードを書いていきます。

import { createClient } from '@supabase/supabase-js';
import { NextApiRequest, NextApiResponse } from 'next';

const handler = async (req: NextApiRequest, res: NextApiResponse) => {
  if (req.method !== 'DELETE') {
    return res.status(405).json({ error: 'Method not allowed' });
  }

  const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
  const supabaseServiceRoleKey = process.env.SERVICE_ROLE_KEY;

  if (!supabaseUrl || !supabaseServiceRoleKey) {
    return res.status(500).json({ error: 'Missing Supabase configuration' });
  }

  const supabaseAdmin = createClient(supabaseUrl, supabaseServiceRoleKey);

  const { id } = req.query;

  const { error } = await supabaseAdmin.auth.admin.deleteUser(id as string);

  if (error) {
    return res.status(500).json({ error: error.message });
  }

  return res.status(200).json({ message: 'User deleted successfully' });
}

export default handler;

pages配下のフロント側から先ほど定義したAPIエンドポイントにアクセスします
この時userを削除してもログインセッションが残ってしまうのでログアウト処理も同時に行います

const onDelete = async () => {
    await supabaseClient.auth.signOut();
    const response = await fetch(`/api/users/${user?.id}`, {
        method: 'DELETE',
    });
    await router.replace('/login');
};

これでログインユーザーを削除できるようになりました

画像登録があるログインユーザーを削除する方法

上記の手順でログインユーザーを削除できるようになりましたがStorage に画像をアップロードしている場合以下のような外部キー制約エラーが出ます。

failed to delete user: update or delete on table "users" violates foreign key constraint "objects_owner_fkey" on table "objects"

このエラーを解消するためにはログインユーザーに関連しているレコードを先に削除する必要があります。
つまりStoreageの画像を削除する必要があります。
ということで以下のリンクに解決策があったのでそのまま使わせていただきました。

https://zenn.dev/panda_program/scraps/828e48b9940f3c

Supabaseでは、PostgreSQLのFunctions(関数)を作成することで、データベース上で定義したロジックを実行することができます。

以下のSQLはauth.uid()で取得した現在のユーザーのIDを持つstorage.objectsテーブルのレコードを削除し削除が成功したら、trueを返すようにしています。

create or replace function public.handle_delete_user_created_objects()
returns boolean as $$
begin
    delete from storage.objects where owner = auth.uid();
    return true;
end;
$$ language plpgsql security definer;

Supabaseの管理ダッシュボードから以下の手順でFunctionsを登録します。

  1. Supabaseの管理ダッシュボードにログインします。
  2. 左側のナビゲーションメニューから「SQL Editer」を選択します。
  3. 「New Query」ボタンをクリックします。
  4. 新しいクエリエリアに上記のSQLコードを貼り付けます。
  5. 「Run」ボタンをクリックします。

上記手順を実行するとpublic.handle_delete_user_created_objects() という関数がデータベースに作成されます。

今登録した関数はAPI Docで呼び出すコードを確認することができます。

let { data, error } = await supabase
  .rpc('handle_delete_user_created_objects')

if (error) console.error(error)
else console.log(data)

これで画像を削除する準備ができましたのでpages配下のフロント側に上記のコードを追記します。

const onDelete = async () => {
    await supabaseClient.auth.signOut();
    const { data, error } = await supabaseClient.rpc(
        'handle_delete_user_created_objects',
    );
    if (error) {
        throw error;
    }
    const response = await fetch(`/api/users/${user?.id}`, {
        method: 'DELETE',
    });
    await router.replace('/login');
};

これでログインユーザーを削除するときに画像も一緒に削除できるようになりました。
その他、ログインユーザーに紐づく情報を一緒に削除する場合は、Supabaseのon delete cascadeを
設定しておくようにしておきましょう。

コメントを残す

入力エリアすべてが必須項目です。メールアドレスが公開されることはありません。

内容をご確認の上、送信してください。