今回は、Database Functionsという機能を使ってSupabaseのデータベースに値を保存する方法を解説していきます。
また、Next.jsからSupabaseにアクセスしてデータを取得して表示する方法についても合わせて解説していきます。
Database Functionsを使って、ユーザーがボタンをクリックした数をSupabaseのDBに保存する
こちらの記事では、ログインしたユーザーがボタンを押した回数をSupabaseに記録するという処理を、SupabaseのDatabase Functionsという機能を使って実装していきます。
開発環境
- macOS Catalina 10.15.7
- Next.js 13.1.6
- Supabase 2.5.0
- Reactstrap 9.1.5
※Next.jsはJavaScriptを使っています。
Supabaseでログイン機能を作成する
Supabaseを使ったログイン機能の実装に関しては、以前にこちらの記事で解説していますので、今回の実装はこちらの内容の続きとして解説していきます。
上記の記事で実装したコードはこちらのリポジトリにアップしてありますので、まずは以下のコマンドでリポジトリの内容をクローンしてください。
git clone https://github.com/masakiwakabayashi/nextjs_supabase_auth
以下のコマンドでパッケージのインストールとプロジェクトを立ち上げを行います。
npm install
npm run dev
また、ログイン機能を作る上でSupabseの管理画面から設定が必要な部分もありますが、それに関しても上記の記事で解説しています。
Supabaseの管理画面からの設定も終わったら、ユーザー登録とログインができるかどうかを確認してください。
ユーザー登録は http://localhost:3000/register から、ログインは http://localhost:3000/login から確認できます。
ログインができることを確認したら、ユーザーがボタンをクリックした回数が表示される画面とクリックするためのボタンを作成していきます。
ページコンポーネントを作成
pagesディレクトリにclick_count.jsというファイルを新しく作成してください。click_count.jsの中身は以下のように書きます。
import styles from '../styles/Home.module.css'
import { Button } from 'reactstrap';
import { useEffect, useState } from 'react';
import { supabase } from '../utils/supabase';
import { useRouter } from 'next/router';
const ClickCountPage = () => {
const router = useRouter();
// ユーザーのメールアドレス
const [currentUserEmail, setcurrentUserEmail] = useState('');
// クリック数
const [clickCount, setClickCount] = useState(0);
// 現在ログインしているユーザーを取得する処理
const getCurrentUser = async () => {
// セッションがある場合はログインしているユーザーを取得し、ない場合はログインページに遷移させる
const { data } = await supabase.auth.getSession()
if (data.session !== null) {
const { data: { user } } = await supabase.auth.getUser()
setcurrentUserEmail(user.email)
} else {
router.push('/login')
}
}
// ページが読み込まれたときにgetCurrentUser()を実行
useEffect(()=>{
getCurrentUser()
},[])
// クリック数を取得する処理
const handleClick = async () => {
setClickCount(clickCount + 1)
}
return (
<div className={styles.card}>
<div style={{ paddingBottom: "1rem" }}>
{ currentUserEmail } でログインしています。
クリック数: { clickCount }
</div>
<Button color="primary" onClick={handleClick}>
クリック
</Button>
</div>
);
}
export default ClickCountPage;
実際に、http://localhost:3000/click_count にアクセスしてみると、ユーザーがボタンをクリックした数が表示されるようにはなっていますが、ページをリロードするとクリック数のカウントがゼロに戻ってしまいます。
ここからは、ページをリロードしてもユーザーがボタンをクリックした数がゼロに戻らないようにするために、
- クリック数をSupabaseのデータベースに保存する処理
- Supabaseのデータベースに保存されているクリック数を画面に表示させる処理
を作成していきます。
Supabaseにボタンのクリック数を保存するためのテーブルを作成する
まずはSupabaseにユーザーがボタンをクリックした数を保存するためのテーブルを作成していきます。
Supabaseにログインしてプロジェクトの管理画面を開いて、サイドバーからデータベースを選択してください。
一番上のNameのところにprofilesと入力します。これが作成するテーブルのテーブル名になります。
次に「Enable Row Level Security (RLS)」をオフにします。
その下にあるColumnsのidにユーザーのidが紐づくようにします。idのNameとTypeの間にあるチェーンのようなマークをクリックしてください。
すると外部キー制約を設定できる画面が表示されるので、authのusersを選択してください。
usersを選択するとどのカラムを参照するのかを選択できるセレクトボックスが表示されるので、「id uuid」を選択してsaveをクリックしてください。
次はクリック数を格納するためのカラムを追加するので「Add column」を選択してください。
新しいカラムの入力欄が表示されるので、Nameをclicks、Typeをint8、Default Valueを0としてSaveをクリックしてください。
テーブルの作成はこれで完了です。次はテーブルのデータを作成します。
まず、サイドバーから「Authentication」を選択してください。
自身でユーザー登録の画面から作成したユーザーが表示されているので、そのユーザーの「User UID」をコピーしておきます(クリックするとコピーできます)。
サイドバーから「Table Editor」を選択してください。
テーブルの一覧に先ほど作成したprofilesテーブルがありますので、それを選択します。
「Insert row」と「Insert column」が出てくるので、「Insert row」を選択します。
idに先ほどコピーした自身で作成したユーザーのuuidを貼り付け、created_atに適当な日時を選択し、clicksの値を0として「Save」をクリックします。
これで自身で作成したユーザーがログインした際にボタンをクリックした数を保存するためのテーブルデータの作成が完了しました。
Database Functionを作成する
では、ここからユーザーがボタンをクリックした数を先ほどのテーブルデータに格納するためのDatabase Functionを作成していきます。
Supabaseのプロジェクト管理画面を開いて、サイドバーから「Database」を選択します。
Databaseの画面の中に「Functions」というところがあるので、そちらを選択します。
次に「Create a new function」を選択します。
すると新しいDatabase Functionの作成画面が表示されるので、「Name of function」にtest_incrementと入力して「Return type」にint8を選択します。
また、Definitionは以下のように書きます。
declare
new_clicks int;
begin
-- ログインしているユーザーのクリック数を取得
select clicks
into new_clicks
from public.profiles
where id = auth.uid();
-- クリック数を増やす
new_clicks = new_clicks + 1;
-- クリック数を更新
update public.profiles
set clicks = new_clicks
where id = auth.uid();
-- 増やしたクリック数をリターン
return new_clicks;
end;
ここまで入力したらConfirmを押して内容を確定させます。Database Functionの作成はこれで完了です。
Next.jsからDatabase Functionを呼び出す
Next.jsから先ほど作成したDatabase Functionを呼び出す処理を作成していきます。
click_count.jsの中身を以下のように編集します。
import styles from '../styles/Home.module.css'
import { Button } from 'reactstrap';
import { useEffect, useState } from 'react';
import { supabase } from '../utils/supabase';
import { useRouter } from 'next/router';
const ClickCountPage = () => {
const router = useRouter();
// ユーザーのメールアドレス
const [currentUserEmail, setcurrentUserEmail] = useState('');
// クリック数
const [clickCount, setClickCount] = useState(0);
// 現在ログインしているユーザーを取得する処理
const getCurrentUser = async () => {
// セッションがある場合はログインしているユーザーを取得し、ない場合はログインページに遷移させる
const { data } = await supabase.auth.getSession()
if (data.session !== null) {
const { data: { user } } = await supabase.auth.getUser()
setcurrentUserEmail(user.email)
} else {
router.push('/login')
}
}
// ページが読み込まれたときにgetCurrentUser()を実行
useEffect(()=>{
getCurrentUser()
},[])
// クリック数を取得する処理
const handleClick = async () => {
// database functionを呼び出す処理
const { data, error } = await supabase.rpc('test_increment')
// エラーが起きたときはコンソールにエラーを表示
if (error) console.error(error)
// 更新されたクリック数をコンソールに表示
console.log(data);
// 画面に表示するクリック数を更新する処理
setClickCount(clickCount + 1)
}
return (
<div className={styles.card}>
<div style={{ paddingBottom: "1rem" }}>
{ currentUserEmail } でログインしています。
クリック数: { clickCount }
</div>
<Button color="primary" onClick={handleClick}>
クリック
</Button>
</div>
);
}
export default ClickCountPage;
変更したのは以下の部分です。こちらの処理でDatabase Functionを呼び出して、ユーザーがボタンを押したときにクリック数が更新されるようにしています。
// クリック数を取得する処理
const handleClick = async () => {
// database functionを呼び出す処理
const { data, error } = await supabase.rpc('test_increment')
// エラーが起きたときはコンソールにエラーを表示
if (error) console.error(error)
// 更新されたクリック数をコンソールに表示
console.log(data);
// 画面に表示するクリック数を更新する処理
setClickCount(clickCount + 1)
}
実際にボタンをクリックすると、Sudpabaseに保存されているクリック数が更新されていることが確認できます。
また、Supabaseの管理画面からもクリック数が更新されていることが確認できます。
Supabaseに保存されているクリック数を取得して、画面に表示させる処理を作成する
続いてはSupabaseに保存されているユーザーのクリック数を取得して画面に表させるためのNext.jsの処理を書いていきます。
click_count.jsの中身を以下のように編集してください。
import styles from '../styles/Home.module.css'
import { Button } from 'reactstrap';
import { useEffect, useState } from 'react';
import { supabase } from '../utils/supabase';
import { useRouter } from 'next/router';
const ClickCountPage = () => {
const router = useRouter();
// ログインユーザーのid
const [currentUserId, setcurrentUserId] = useState('');
// ユーザーのメールアドレス
const [currentUserEmail, setcurrentUserEmail] = useState('');
// クリック数
const [clickCount, setClickCount] = useState(0);
// 現在ログインしているユーザーを取得する処理
const getCurrentUser = async () => {
// セッションがある場合はログインしているユーザーを取得し、ない場合はログインページに遷移させる
const { data } = await supabase.auth.getSession()
if (data.session !== null) {
const { data: { user } } = await supabase.auth.getUser()
setcurrentUserId(user.id)
setcurrentUserEmail(user.email)
} else {
router.push('/login')
}
}
// ページが読み込まれたときにgetCurrentUser()を実行
useEffect(()=>{
getCurrentUser()
},[])
// supabaseのclicksテーブルからクリック数を取得する処理
const getClickCount = async () => {
// supabaseのprofilesテーブルから現在ログインしているユーザーのclicksカラムの値を取得
const { data, error } = await supabase
.from('profiles')
.select('clicks')
.eq('id', currentUserId)
.single();
// エラーが起きたときにコンソールに出力
if (error) console.error(error)
// supabaseから取得したクリック数をclickCountにセット
setClickCount(data.clicks)
}
// ログインしているユーザーを取得できているときだけgetClickCount()を実行
if (currentUserId !== '') {
getClickCount()
}
// クリック数を取得する処理
const handleClick = async () => {
// database functionを呼び出す処理
const { data, error } = await supabase.rpc('test_increment')
// エラーが起きたときはコンソールにエラーを表示
if (error) console.error(error)
// 更新されたクリック数をコンソールに表示
console.log(data);
// 画面に表示するクリック数を更新する処理
setClickCount(data)
}
return (
<div className={styles.card}>
<div style={{ paddingBottom: "1rem" }}>
{ currentUserEmail } でログインしています。
クリック数: { clickCount }
</div>
<Button color="primary" onClick={handleClick}>
クリック
</Button>
</div>
);
}
export default ClickCountPage;
こちらの部分がSupabaseに保存されているクリック数を取得する処理です。
// supabaseのclicksテーブルからクリック数を取得する処理
const getClickCount = async () => {
// supabaseのprofilesテーブルから現在ログインしているユーザーのclicksカラムの値を取得
const { data, error } = await supabase
.from('profiles')
.select('clicks')
.eq('id', currentUserId)
.single();
// エラーが起きたときにコンソールに出力
if (error) console.error(error)
// supabaseから取得したクリック数をclickCountにセット
setClickCount(data.clicks)
}
// ログインしているユーザーを取得できているときだけgetClickCount()を実行
if (currentUserId !== '') {
getClickCount()
}
ここまでできたらページをリロードしても表示されているクリック数がゼロに戻らないことを確認してみてください。
まとめ
Database Functionsを使えば、SQLの処理を事前に用意しておき、Next.jsで用意しておいた処理を実行することができる。
今回の解説に使ったコードはこちらのリポジトリに上げています。
https://github.com/masakiwakabayashi/nextjs_supabase_db_functions