8
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Next.js】Database Functionsを使ってSupabaseのデータベースに値を保存する方法を解説

Last updated at Posted at 2023-03-02

今回は、Database Functionsという機能を使ってSupabaseのデータベースに値を保存する方法を解説していきます。

また、Next.jsからSupabaseにアクセスしてデータを取得して表示する方法についても合わせて解説していきます。

Database Functionsを使って、ユーザーがボタンをクリックした数をSupabaseのDBに保存する

こちらの記事では、ログインしたユーザーがボタンを押した回数をSupabaseに記録するという処理を、SupabaseのDatabase Functionsという機能を使って実装していきます。

スクリーンショット 2023-02-08 16のコピー.png

開発環境

  • 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の中身は以下のように書きます。

pages/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 にアクセスしてみると、ユーザーがボタンをクリックした数が表示されるようにはなっていますが、ページをリロードするとクリック数のカウントがゼロに戻ってしまいます。
スクリーンショット 2023-02-08 16.png

ここからは、ページをリロードしてもユーザーがボタンをクリックした数がゼロに戻らないようにするために、

  • クリック数をSupabaseのデータベースに保存する処理
  • Supabaseのデータベースに保存されているクリック数を画面に表示させる処理

を作成していきます。

Supabaseにボタンのクリック数を保存するためのテーブルを作成する

まずはSupabaseにユーザーがボタンをクリックした数を保存するためのテーブルを作成していきます。

Supabaseにログインしてプロジェクトの管理画面を開いて、サイドバーからデータベースを選択してください。
スクリーンショット 2023-02-08 13.46.41.png

右上にある「new Table」をクリックします。
スクリーンショット 2023-02-08 11.32.04.png

クリックすると以下のような画面になります。
スクリーンショット 2023-02-08 13.51.55.png

一番上のNameのところにprofilesと入力します。これが作成するテーブルのテーブル名になります。
スクリーンショット 2023-02-08 13.52.39.png

次に「Enable Row Level Security (RLS)」をオフにします。
スクリーンショット 2023-02-08 11.34.28.png

その下にあるColumnsのidにユーザーのidが紐づくようにします。idのNameとTypeの間にあるチェーンのようなマークをクリックしてください。
スクリーンショット 2023-02-08 14.00.41.png

すると外部キー制約を設定できる画面が表示されるので、authのusersを選択してください。
スクリーンショット 2023-02-08 14.04.32.png

usersを選択するとどのカラムを参照するのかを選択できるセレクトボックスが表示されるので、「id uuid」を選択してsaveをクリックしてください。

スクリーンショット 2023-02-08 14.07.25.png
これでテーブルの外部キー制約が設定できました。

次はクリック数を格納するためのカラムを追加するので「Add column」を選択してください。
スクリーンショット 2023-02-08 14.10.42.png

新しいカラムの入力欄が表示されるので、Nameをclicks、Typeをint8、Default Valueを0としてSaveをクリックしてください。

スクリーンショット 2023-02-08 14.19.12.png

テーブルの作成はこれで完了です。次はテーブルのデータを作成します。

まず、サイドバーから「Authentication」を選択してください。
スクリーンショット 2023-02-08 14.56.05.png

自身でユーザー登録の画面から作成したユーザーが表示されているので、そのユーザーの「User UID」をコピーしておきます(クリックするとコピーできます)。
スクリーンショット 2023-02-08 14.png

サイドバーから「Table Editor」を選択してください。
スクリーンショット 2023-02-08 15.png

テーブルの一覧に先ほど作成したprofilesテーブルがありますので、それを選択します。
スクリーンショット 2023-02-08 14.40.07.png

「Insert」のボタンをクリックします。
スクリーンショット 2023-02-08 11.37.00.png

「Insert row」と「Insert column」が出てくるので、「Insert row」を選択します。
スクリーンショット 2023-02-08 12.02.12.png

idに先ほどコピーした自身で作成したユーザーのuuidを貼り付け、created_atに適当な日時を選択し、clicksの値を0として「Save」をクリックします。
スクリーンショット 2023-02-08 12.02.46.png

これで自身で作成したユーザーがログインした際にボタンをクリックした数を保存するためのテーブルデータの作成が完了しました。

Database Functionを作成する

では、ここからユーザーがボタンをクリックした数を先ほどのテーブルデータに格納するためのDatabase Functionを作成していきます。

Supabaseのプロジェクト管理画面を開いて、サイドバーから「Database」を選択します。
スクリーンショット 2023-02-08 15.13.08.png

Databaseの画面の中に「Functions」というところがあるので、そちらを選択します。
スクリーンショット 2023-02-08 15.13.47.png

次に「Create a new function」を選択します。
スクリーンショット 2023-02-08 15.54.06.png

すると新しいDatabase Functionの作成画面が表示されるので、「Name of function」にtest_incrementと入力して「Return type」にint8を選択します。
スクリーンショット 2023-02-08 15.55.30.png

また、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の中身を以下のように編集します。

pages/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を呼び出して、ユーザーがボタンを押したときにクリック数が更新されるようにしています。

pages/click_count.js
  // クリック数を取得する処理
  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に保存されているクリック数が更新されていることが確認できます。
スクリーンショット 2023-02-08 17.png

また、Supabaseの管理画面からもクリック数が更新されていることが確認できます。
スクリーンショット 2023-02-08 17.40.00.png

Supabaseに保存されているクリック数を取得して、画面に表示させる処理を作成する

続いてはSupabaseに保存されているユーザーのクリック数を取得して画面に表させるためのNext.jsの処理を書いていきます。

click_count.jsの中身を以下のように編集してください。

pages/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に保存されているクリック数を取得する処理です。

pages/click_count.js
  // 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()
  }

ここまでできたらページをリロードしても表示されているクリック数がゼロに戻らないことを確認してみてください。

スクリーンショット 2023-02-08 17のコピー.png

まとめ

Database Functionsを使えば、SQLの処理を事前に用意しておき、Next.jsで用意しておいた処理を実行することができる。

今回の解説に使ったコードはこちらのリポジトリに上げています。
https://github.com/masakiwakabayashi/nextjs_supabase_db_functions

8
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?