はじめに
こんにちは!かほです♪🐥
現在、本業では技術広報業務やエンジニア業務などに従事しています。
今回の記事では、Next.js×Supabaseを用いたアプリ開発における投稿の削除機能の実装について説明します。
前提
今回は、投稿の削除機能の実装を行います。
以下の記事の内容はすでに実装済みですので、まだ未実装の方は参考にしてみてください♪🐥
Next.js×Supabase×Vercelの連携方法について知りたい方
認証機能の実装について知りたい方
CRUD処理(投稿機能、一覧機能)の実装について知りたい方
この記事の読者対象
- Next.js×SupabaseでCRUD処理(投稿の削除機能)を含むアプリを作ってみたい方
- SupabaseのRLS設定について知りたい方
- Supabaseを今後使ってみたい方
- Next.jsを使って高速でアプリを作りたい方
開発環境
"dependencies": {
"@supabase/supabase-js": "^2.2.1",
"eslint": "8.28.0",
"eslint-config-next": "13.0.6",
"next": "13.0.6",
"react": "18.2.0",
"react-dom": "18.2.0",
},
"devDependencies": {
"@types/node": "^18.11.13",
"@types/react": "18.0.26"
}
認証済みアカウントのuser_idを投稿に設定しよう!
投稿の削除機能を実装するにあたり、投稿のユーザーIDを取得する必要があります。
その際に必要なコードの全体像を下記に記載します。
import { User } from "@supabase/supabase-js";
export default function Top() {
const [user, setUser] = useState<User | null>(null);
useEffect(() => {
(async () => {
await indexPost();
const { data } = await supabase.auth.getUser();
setUser(data.user);
})();
}, []);
const addPost = async (e) => {
if (user === null) return;
e.preventDefault();
try {
const { error } = await supabase.from("posts").insert([
{
user_id: user.id,
title: newTitle,
content: newContent,
},
]);
if (error) throw error;
await indexPost();
setNewTitle("");
setNewContent("");
} catch (error) {
alert("データの新規登録ができません");
}
};
}
コードを上から順に説明します。
getUser()
を使用し、認証済みユーザーのデータを取得します。
getUser()
を使用する方法は、下記のSupabase公式ドキュメントにも記載されていますので、ご興味のある方はご参照ください🙌
取得したユーザーのデータを状態管理するため、setUser(data.user)
を記載します。
const [user, setUser] = useState<User | null>(null);
useEffect(() => {
(async () => {
await indexPost();
const { data } = await supabase.auth.getUser();
setUser(data.user);
})();
}, []);
次に投稿したものにuser_id
を付与します。
上記の記事で、投稿機能を実装していますが、insert()
を用いて投稿データを挿入する際に
user_id
を引数に入れます。user.id
のuserは上記で取得し状態管理している認証済みのユーザー情報です。
const { error } = await supabase.from("posts").insert([
{
user_id: user.id,
title: newTitle,
content: newContent,
}
]);
実装後、投稿してみると下記の画像のように、postのuser_idに値が入ります!
投稿後はuser_idが取得できているか確認しましょう♪🐥
RLSを設定しよう
Policyを作成しよう
次にPolicyを作成します。
- RLS有効化を確認した画面の右端にある
New Policy
というボタンを押します。 - ボタンを押すと下記の画面のように選択肢が2つ出てきます。
テンプレートを使用してPolicyを作成したい方はGet started quickly
を選択し、完全にゼロからPolicyを作成したい方はFor full customization
を選択します。
. - 今回はテンプレートから作成したいので、
Get started quickly
を選択しました。下記のような画面ができます。上から取得
、挿入
、更新
、削除
の機能に対するSQLテンプレートがあります。
.
削除機能に対する認可を設定しよう
次に削除機能に対する認可の設定を行います。
- 削除機能に対する認可を設定したいので、4つのSQLテンプレートの中から
Enable read access to everyone
(上から4つ目)を選択し、Use this template
ボタンを押します。すると下記の画面が出てきます。
. -
Policy name
を設定します。今回は認証済みのアカウントでかつ、投稿者本人のみが投稿できる
という名前を設定します。 -
Allowed operation
を設定します。全てのデータを選択して表示させたいのでDELETE
を選択します。 -
Target roles
を設定します。今回は認証済みのアカウントを対象とした認可のため、選択肢からauthenticated
を選択します。 -
USING expression
を設定します。今回は、投稿者本人のみが投稿を削除できる状態にしたいので、認証済みのアカウント(auth.uid())と投稿のユーザー(post.user_id)が一致しているという意味でauth.uid() = posts.user_id
と書きます。 - 右下の
Review
のボタンを押します。設定したSQL情報が下記の画面のように出てくるため、情報に間違えがなければSave policy
を押してPolicyを保存します。
Policyの作成を確認しよう
削除機能に対するPolicyの設定が終わりました。
Policyの設定が完了している場合は、下記の画像のように行として表示されるためご確認ください。また、Policyを編集・削除する場合は右端の縦3点のボタンを押すと可能です。
削除機能を実装しよう
次に削除機能についての説明を行います。
下記がtop.tsx
の削除機能における全体像です。
const handleDelete = async (postID: number) => {
try {
const res = await supabase.from("posts").delete().eq("id", postID);
const { data: posts } = await supabase.from("posts").select("*");
setPosts(posts);
} catch (error) {
alert(error.message);
}
};
return (
<>
<div className={styles.container}>
<Head>
<title>トップページ</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<div>
<h1>トップページ</h1>
<div>
<table>
<thead>
<tr>
<th>投稿日</th>
<th>タイトル</th>
<th>内容</th>
</tr>
</thead>
<tbody>
{posts.map((post) => (
<tr key={post.id}>
<td>{post.created_at.substr(0, 10)}</td>
<td>{post.title}</td>
<td>{post.content}</td>
<td>
<button
onClick={() => handleDelete(post.id)}
className="border-gray-300 border-2 rounded p-1 w-12"
>
削除
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</main>
<footer className={styles.footer}></footer>
</div>
</>
);
削除機能の実装方法について、コードを上から順に説明します。
次にsupabase.tsを呼び出し、実装を行います。
今回はdelete()
を使用し、postsテーブルのデータを削除します。
from
の引数にテーブル名を記載し、eq
の引数にテーブルのidと削除したいデータidの変数を記載します。
delete()
を使用する方法は、下記のsupabase公式ドキュメントにも記載されていますので、ご興味のある方はご参照ください🙌
データを削除した後に、削除していないデータを表示したいため、
select()
を用いてsupabaseのデータを取得し、setPosts(posts)
を用いてデータを表示します。
try-catch
構文を用いることにより、例外処理を行い、エラーが出た場合の対処を行います。
const handleDelete = async (postID: number) => {
try {
const res = await supabase.from("posts").delete().eq("id", postID);
const { data: posts } = await supabase.from("posts").select("*");
setPosts(posts);
} catch (error) {
alert(error.message);
}
};
削除機能に関わるマークアップは下記です。
handleDelete
メソッドを呼び出し、引数に削除したデータのidを入れます。
<button
onClick={() => handleDelete(post.id)}
className="border-gray-300 border-2 rounded p-1 w-12"
>削除</button>
削除ができたか確認してみよう!
では、最後にデモ動画で投稿の削除ができたかを確認します。
すでにアカウントが認証済みの段階から、投稿の登録~削除を行いました!
「投稿を投稿者本人が削除できる」ことを確認できました!成功です🎉😊
.
最後に
これで実装はおしまいです!
今回は、Next.js×Supabaseを用いたアプリ開発における投稿の削除機能の実装について説明しました。今後は主にフロントエンド周辺、コミュニティ運営、Tech PR(技術広報)の記事を中心に書いていく予定ですので、気になる方はぜひフォローをよろしくお願いします♪ではでは〜🐥
Twitterアカウント:https://twitter.com/kaho_eng
参考資料