13
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?

Next.js 13 App router と Supabase での4つのアクセス方法

Last updated at Posted at 2023-08-29

2024年6月2日現在

現在はSupabaseのクライアント作成に @supabase/supabase-js を利用しています。

この記事の情報は古く、

import { createServerComponentClient } from "@supabase/auth-helpers-nextjs"

このように @supabase/auth-helpers-nextjs を利用してSupabaseのクライアントを作成しています。

Supabase公式のリポジトリ

masakinihirota/with-supabase

※オリジナルのリポジトリが更新され、新バージョンになりました、
この記事を書いた時点のリポジトリは保存してあったので、このリポジトリを公開します。

このリポジトリで4つのアクセス方法を確認できます。

Supabaseへの 4種類のアクセス方法

  • クライアントから Supabaseのデータを取得する方法
  • ルートハンドラーで Supabaseのデータを取得する方法
  • Server ActionsでSupabaseのDBを操作する方法
  • サーバーコンポーネントで Supabaseのデータを取得する方法

これら4種類のアクセス方法を紹介します。

現時点での Server Actions について (2023年8月31日)

Next.js の Server Actions は先日アルファが取れたようです。
Next.jsの公式ドキュメントで確認。ただし、ベータ版とも書かれていませんでした。
APIの関数として紹介されています。

現在、実験的機能のフラグをtrueにする事で利用できます。

next.config.js
module.exports = {
  experimental: {
    serverActions: true,
  },
}

この記事の主旨

この記事ではNext.jsが公式で用意しているSupabaseのサンプルを使って、
Supabaseのデータを取得する方法を解説します。

Next.js公式examples集を分類(2023年7月版)
https://qiita.com/masakinihirota/items/c4c8931d7067349006ef

Next.jsのサンプル集の中の with-supabase を見ていきます。
このサンプルは Supabase 側でも 推奨されています。

next.js/examples/with-supabase at canary · vercel/next.js

※このサンプルは認証機能付きです。
※2023年12月26日現在リポジトリの内容は変更されてしまいました。

環境

Windows10
VSCode
Supabase

前提知識

Supabaseのアカウント作成済み。
Supabaseの環境変数の取得方法を理解している。
Supabaseのダッシュボードが使えるようになっている。

インストール

npx create-next-app -e with-supabase

※このリポジトリは新バージョンになってしまいました。

環境変数の設定

.env.local
NEXT_PUBLIC_SUPABASE_URL=https://z************z.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=ey**************************74

これで設定が完了していれば

npm run dev

を実行すると

で起動の確認が出来ます。

ユーザーを作成してログインが出来ることを確認してください。

参考

Supabase ローカル開発環境 + サーバー運用を想定 2023 - Qiita

※今回は Supabaseのローカル環境を必要としません。

データベースの初期化

マイグレーションファイルをサーバーに適用する方法

このリポジトリにはサンプル用のSQLがすでに用意されています。
それをSupabaseに適用します。

Supabaseのダッシュボードを開きます。

SQL Editorを開きます。

supabase\migrations\20230618024722_init.sql

supabase\migrations\20230618024722_init.sql
create table if not exists todos (
  id uuid default gen_random_uuid() primary key,
  created_at timestamp with time zone default timezone('utc'::text, now()) not null,
  title text,
  is_complete boolean default false,
  user_id uuid references auth.users default auth.uid()
);

-- Set up Row Level Security (RLS)
-- See https://supabase.com/docs/guides/auth/row-level-security for more details.
alter table todos
  enable row level security;

create policy "Authenticated users can select todos" on todos
  for select to authenticated using (true);

create policy "Authenticated users can insert their own todos" on todos
  for insert to authenticated with check (auth.uid() = user_id);

を貼り付けます。

右下の 「RUN CTRL」ボタンを押して 成功したら メッセージ ↓Successを確認できます。

「Success. No rows returned」

これで todos テーブルが出来ました。

※SupabaseのCLIからサーバーに直接適用できるかどうか調べてみましたが、ローカル環境を作らずに直接サーバーに適用させる方法はわかりませんでした。
ですので上記のようにSQL文をコピペしています。

通常、SupabaseのCLIは、ローカル環境でマイグレーションを作成し、DBでテーブルを作成して、シードファイルを読み込ませて、動作確認ができたらサーバーにPUSHしています。

ローカルからならたくさんのマイグレーションファイルを作ってもPUSH出来ますし、一つにまとめるコマンドもあるようですからローカルからサーバーに反映させるのには問題ありません。

シードファイルをサーバーに適用する方法

supabase\seed.sql

supabase\seed.sql
insert into todos(title)
values
  ('Create Supabase project'),
  ('Create Next.js app from Supabase Starter template'),
  ('Keep building cool stuff!');

シードファイルもテーブル作成と同様にSQL文を貼り付けて実行します。

Supabaseのダッシュボードの Table Editor で todosテーブルを選択すると。
データも入っていることが確認できます。

これでSupabase側の下準備は完了です。

これからNext.jsのコードからSupabaseにアクセスして
そのデータを表示するまでを実践します。


Next.jsでのコンポーネント

サンプルには4つのコンポーネントが用意されています。
この4つにはSupabaseにアクセスする4種類のパターンが用意されています。

4種類のアクセス方法

クライアントから Supabaseのデータを取得する方法

useEffectを利用してデータを取得しています。
Supabaseはクライアントから直接データを取得できます。
クライアントコンポーネントにしています。

ルートハンドラーで Supabaseのデータを取得する方法

Next.jsのルーティング機能を使用してHTTPリクエストを送信してデータを取得しています。
Supabaseから直接データを取得しています。

ルートハンドラー
Webアプリケーションにおいて、特定のURLに対するリクエストを処理するための関数です。
ルートハンドラーは、リクエストを受け取り、適切なレスポンスを返すために使用されます。

Server ActionsでSupabaseのDBを操作する方法

このサンプルではインサート関数を作ってタイトルを保存しています。
Supabaseから直接データを挿入しています。

サーバーコンポーネントで Supabaseのデータを取得する方法

このサンプルではサーバーコンポーネントのクライアントを使用しています。
サーバーコンポーネントからデータを取得しているので、データの取得は高速で、セキュリティリスクは少ないです。

サーバーコンポーネント
サーバー側でのみ実行されるコンポーネントです。

4つのコンポーネント

app\_examples\client-component

app\_examples\route-handler

app\_examples\server-action

app\_examples\server-component

クライアントコンポーネント
ルートハンドラー
サーバーアクション
サーバーコンポーネント
の4種類です。

※Next.jsの App router のルールとして フォルダ名の先頭に _[アンダーバー]が
ついているとそのフォルダは無視されます。

app\_examples

このアンダーバーを消して

app\examples

にフォルダ名を変更します。

※VSCode上でフォルダ名を変更すると時間がかなりかかるので、
一旦VSCodeを閉じてエクスプローラー上から変更して、再度VSCodeを起動します。

最初にトップページにそれぞれのコンポーネントにリンクを貼っておきます。

ログイン部分の機能だけ残して不要な部分を削除します。
(見た目を綺麗にするだけです)

app\page.tsx

app\page.tsx
import { createServerComponentClient } from "@supabase/auth-helpers-nextjs"
import { cookies } from "next/headers"
import Link from "next/link"
import LogoutButton from "../components/LogoutButton"

export default async function Index() {
  const supabase = createServerComponentClient({ cookies })

  const {
    data: { user },
  } = await supabase.auth.getUser()

  return (
    <>
      <nav className="flex justify-center w-full h-16 border-b border-b-foreground/10">
        <div className="flex items-center justify-between w-full max-w-4xl p-3 text-sm text-foreground">
          <div />
          <div>
            {user ? (
              <div className="flex items-center gap-4">
                Hey, {user.email}!
                <LogoutButton />
              </div>
            ) : (
              <Link
                href="/login"
                className="px-4 py-2 no-underline rounded-md bg-btn-background hover:bg-btn-background-hover"
              >
                Login
              </Link>
            )}
          </div>
        </div>
      </nav>
      <Link href="./examples/client-component">Client Component Example</Link>
      <Link href="./examples/route-handler">Route Handler Example</Link>
      <Link href="./examples/server-action">Server Action Example</Link>
      <Link href="./examples/server-component">Server Component Example</Link>
    </>
  )
}

リンクを貼るだけでコードの作業は完了です。

あとは
npm run dev
でローカル環境を立ち上げて

をブラウザで表示します。

ログインをします。
※ログインはそれぞれ各自で設定してください。

そうするとトップページに4つのリンクが表示されます。

Client Component Example
Route Handler Example
Server Action Example
Server Component Example

※ダッシュボードの Table Editor を開き todos テーブルのデータと見比べてください。
※Server Action Exampleはなにか文字列を入力してリターンキーを押すと、データがテーブルに登録されます。リターンキーを押しすぎるとそのまま複数のデータが登録されるので注意してください。

Supabaseのアクセス方法の解説 (おまけ)

Client Component Example

クライアントコンポーネントです。
なので"use client"とディレクティブが書かれています。

  const [todos, setTodos] = useState<any[]>([])

Hooks useState の基本的な使いかたなので説明の必要はありません。

  const supabase = createClientComponentClient()

createClientComponentClient を使用してクライアントを作っています。

※Supabaseのクライアントつくると Supabaseへアクセスする事ができるようになります。

  useEffect(() => {
    const getTodos = async () => {
      const { data } = await supabase.from("todos").select()
	// データがあるのなら
      if (data) {
        setTodos(data)
      }
    }

    getTodos()
  }, [supabase, setTodos])

↑Supabaseにアクセスするために非同期処理 async/await を利用しています。

supabase.from("todos").select()

↑このコードはSupabaseにアクセスしています。
見た目通り todos テーブルから select 関数で 全てのカラムを取得しています。

  }, [supabase, setTodos])

↑この行は useEffect の第2引数で 依存配列です。
どちらかが変更された場合に再度実行されます。
これも useEffect の基本的な使い方です。

return

{JSON.stringify(todos, null, 2)}

return文は取得したデータを表示するだけの役割です。

特に難しいところはありませんでした。

Route Handler Example

Next.jsのルーティング機能を使用して、HTTPリクエストを受け取り、適切なルートにルーティングするための関数が定義されたファイルです。このファイルには、GETHTTPメソッド関数が定義されており、リクエストを受け取り、適切な処理を行うためのコードが記述されています。

GET 関数は、HTTP GETリクエストを受け取り、Supabaseを使用してデータを取得するための処理を行います。

このファイルは、Next.jsのルーティング機能を使用して、サーバーサイドでのAPIエンドポイントを簡単に作成するためのサンプルです。

ルートハンドラーのクライアントは
createRouteHandlerClient
を利用してクライアントを作成しています。

  const supabase = createRouteHandlerClient({ cookies })

※クッキーを使用してクライアントを作成しています。

  const { data: todos } = await supabase.from('todos').select()

これは前項のデータ取得方法と同じです。

  return NextResponse.json(todos)

取得したデータをjsonで返しています。

特に難しいところはありませんでした。

Server Action Example

このサンプルのメイン Server Actions の利用方法です。
このサンプルでは、Supabaseの todos テーブルにデータを登録しています。

このコンポーネントは、createServerActionClient 関数を使用して、Supabaseのクライアントを初期化し、useEffect フックを使用して、コンポーネントがマウントされたときにデータを取得する処理を行っています。

取得したデータは、setTodos 関数を使用して、todos ステートに保存され、JSON.stringify メソッドを使用して、JSON形式で表示されます。

revalidatePath()
この関数は、ページのパスを受け取り、そのパスに関連するデータを再検証するための処理を行います。

Next.js Server Actions と revalidate 周りの挙動を確認する

サーバーアクションのクライアントは
createServerActionClient
を利用してクライアントを作成しています。

ServerAction()関数は非同期関数です

  const addTodo = async (formData: FormData) => {
    "use server"
    const title = formData.get("title")

↑この場所で"use server" ディレクティブが宣言されています。

このファイルはサーバー側です、しかしこのように "use server"とディレクティブを宣言しないと関数をクライアントコンポーネントに渡すことは出来ません。

    <form action={addTodo}>

この場所で関数を渡しています。

    const title = formData.get("title")

↑この行で 、フォームから送信されたデータからタイトルを取得しています。
formDataという変数は、フォームのデータを表すオブジェクトです。get()メソッドを使用して、titleという名前のフィールドの値を取得します。

タイトルが存在する場合

      const supabase = createServerActionClient({ cookies })

クライアントを作成しています。

今回はクライアントからDBへ直接挿入をしています。

await supabase.from("todos").insert({ title })

↑この1行だけでフォームに入力したタイトルを直接DBに登録しています。
コードは非常にシンプルです。

      revalidatePath("/server-action-example")

revalidatePathをつかい、データが更新されていたらページのデータが最新のものに更新されます。

  return (
    <form action={addTodo}>
      <input name="title" />
    </form>
  )

↑return文でフォームに ServerAction関数である addTodo が登録されています。

Server Component Example

このコンポーネントは、createServerComponentClient 関数を使用して、Supabaseのクライアントを初期化し、useEffect フックを使用して、コンポーネントがマウントされたときにデータを取得する処理を行っています。

サーバーコンポーネントなので、
データへのアクセス速度は早く、
セキュリティリスクは少なく、
安全にデータを扱うことが可能です。

  const supabase = createServerComponentClient({ cookies })

Supabaseのクライアントを作っています。

  const { data: todos } = await supabase.from('todos').select()

supabase.from('todos').select()
Supabaseの todos テーブルから すべてのデータを取得しています。

  return <pre>{JSON.stringify(todos, null, 2)}</pre>

JSON.stringify()関数は、JavaScriptオブジェクトをJSON文字列に変換するために使用されます。
この関数は、3つの引数を受け取ります。最初の引数は変換するオブジェクトであり、2番目の引数は置換関数であり、3番目の引数はインデントレベルです。この場合、todosオブジェクトが変換され、インデントレベルが2に設定されています。

App routerの特性を利用して サーバからDBにアクセスをしてもらって
それをブラウザで表示しています。

これも特に難しい処理はありません。

以上が4つのDBへのアクセス方法になります。

ログアウト

ログアウトすると、ブラウザ上にはデータが表示されません。
Server Actionsからもデータを登録できません。
これは RLS が効いているからです。

最初に使用したSQL文で、テーブルを作成した後半に RLS を有効にするコードと
RLSの使用条件を設定したコードが書かれています。

13
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
13
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?