0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

「Supabase × Next.js で ToDo リストを作ってみた」 【学習記録】

Last updated at Posted at 2025-08-21

1.はじめに

こんにちは!
未経験からエンジニアを目指して学習中のhayateです。

今回は 「Supabase × Next.js で ToDoリストを作ってみた」
をテーマに進めていきます。

「バックエンドの経験がなくても、データベースを使ってみたい」
「フロントエンドとDBをつなげて、動くアプリを体験したい」

という方に向けて、シンプルな流れで実践していきます。

最終的には、入力フォームからタスクを追加し、
画面に一覧表示 → 削除までできるToDoリスト をゴールにします👇

スクリーンショット 2025-08-21 14.56.34.png


🎯 今回のゴール

  • Supabaseで新規プロジェクトを作成
  • todos テーブルを用意(id / task / created_at)
  • Next.jsプロジェクトを準備してSupabaseと接続
  • タスクを入力フォームから追加(insert)
  • 画面に一覧表示(select)
  • 削除ボタンでタスクを削除(delete)

⚙️ 開発環境と事前準備

  • Node.js v◯◯
  • npm v◯◯
  • Next.js(create-next-appで作成)
  • Supabase(https://supabase.com/)
  • Tailwind CSS(UI調整用、任意)

2. 手順の全体像

今回の流れは以下の通りです。

DB準備 → Supabaseで新規プロジェクトを作成し、todosテーブルを用意
環境準備 → create-next-app で Next.js プロジェクトを作成
接続設定 → Supabaseクライアントをセットアップし、環境変数を設定
タスク追加 → 入力フォームからタスクをDBに登録(insert)
タスク表示 → Supabaseからデータを取得して一覧表示(select)
タスク削除 → 削除ボタンを実装してDBからデータを削除(delete)
動作確認 → npm run dev でブラウザからToDoリストを操作

3. 準備編

1. DB準備:Supabaseで新規プロジェクトを作成する

まずはデータベースを用意します。
Supabase を使って新規プロジェクトを作成し、ToDoリスト用の todos テーブルを準備していきます。

① 新規プロジェクトを作成

Supabaseのダッシュボードの「New Project」をクリックし、
新しいプロジェクトの作成画面を開き、プロジェクトを作成します。

スクリーンショット 2025-08-19 16.51.21.png

今回の場合は記事のテーマがToDo リストを作ることなので、
プロジェクト名は上のスクショ画面のとおり「todo-app」とします。

todos テーブルを作成

プロジェクトが作成されたら、左メニューの「Table Editor」を選択します。
New Table」から todos というテーブルを作成します。
カラム例(最低限でOK):

カラム名 備考
id int8 主キー、自動採番(Primary Key, Identity)
task text タスク内容を保存 ※
created_at timestamp デフォルト値:now()

※task カラムのところはユーザー入力で値が入るようにするため、
 Default Value は何も設定せずにこのまま進めます。

スクリーンショット 2025-08-19 17.11.43.png

👉 これで、ToDoリストに必要なデータを保存する準備が整いました。

2. Next.jsプロジェクト作成

まずはターミナルを開き、bashモードに切り替えた上で、
以下のコードを入力します。

npx create-next-app@latest todo-app ※
cd todo-app

 ※途中の質問については、全てy を入力して進めてます

「npx create-next-app@latest todo-app」と入力した後の
自動的に create-next-app がインストールされるまでの様子がこちら👇です。

スクリーンショット 2025-08-19 19.43.17.png

インストールが完了すると、自動的に todo-app というディレクトリに
Next.js プロジェクトが作成されます。

今回の記事では、わかりやすさのために Supabase のプロジェクト名と
Next.js のプロジェクト名をどちらも todo-app に統一
しています。
実際には別々の名前でも問題ないようですが、学習用やチュートリアルの段階では
同じ名前にしておくと混乱しにくいので、統一して進めていきます。

npm run dev を実行すると、ブラウザに Next.js の初期画面が表示されます。

スクリーンショット 2025-08-19 19.59.59.png

この画面が表示されれば、Next.js プロジェクトは正しく作成され、
開発サーバーも動作していることが確認できました。

3. Supabaseクライアントの設定

このフェーズではプロジェクトを作成した後、Next.js から
データベースに接続できるようにするための工程となります。

まずは、Next.js から Supabase に接続するための 環境変数を作成していきます。

① 環境変数を作成

Next.js プロジェクトのルートに .env.local を作成し、以下を記述します。

NEXT_PUBLIC_SUPABASE_URL=(SupabaseプロジェクトのURL)
NEXT_PUBLIC_SUPABASE_ANON_KEY=(Anonキー)

SupabaseプロジェクトのURLとAnonキーについてはそれぞれ
Supabaseの「Settings」からコピペして進めていきました。

環境変数の設定が完了したら、いよいよ Next.js から Supabase
接続できるようにクライアントを作成していきます。

ここで作るクライアントを使うことで、ページやコンポーネントから
簡単にデータベース操作(CRUD)が可能になります。

② Supabase クライアントを作成

前の手順で .env.localSupabaseプロジェクトのURL
Anonキー の設定が完了したので、
次はこれらの情報を使って Next.js 側で Supabase クライアントを作成します。

lib/supabaseClient.ts というファイルを作成し、以下の内容を記述します。

import { createClient } from '@supabase/supabase-js';

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!;

export const supabase = createClient(supabaseUrl, supabaseAnonKey);

このようにVSCode上に入力&保存すればここのフェーズは完了です。

スクリーンショット 2025-08-19 20.35.11.png

③ 必要なパッケージをインストール

続けて、VSCodeのターミナル上(bashモードに切り替えます)に
npm install @supabase/supabase-jsと入力します。

スクリーンショット 2025-08-19 20.48.59.png

added 13 packages → @supabase/supabase-js が正しくインストールされた
と表記されているので、Next.js から Supabase の API を呼び出せる状態 になりました。

4. 開発編(実装+確認)

いよいよ実際にコードを書いて、CRUD(Create, Read, Update, Delete)
の基礎を作っていく工程です。
ToDoリストを題材にして、次のような流れで少しずつ機能を実装していきます。

1. タスク追加(Create)

このフェーズでは入力フォームからタスクをDBに登録する処理を実装する工程です。

現段階でsrc/app/page.tsx には Next.js のデフォルト画面用コードが
入っていますが、今回のToDoアプリには不要です。

そこで削除し、以下のコードの内容に書き換えます。(「タスク追加フォーム」)

src/app/page.tsx
"use client";

import { useState } from "react";
import { supabase } from "../lib/supabaseClient";

export default function Home() {
  const [task, setTask] = useState("");
  const [loading, setLoading] = useState(false);

  const addTask = async (e: React.FormEvent) => {
    e.preventDefault();
    if (!task.trim()) return;

    setLoading(true);

    const { error } = await supabase
      .from("tasks") // ← テーブル名に注意
      .insert([{ title: task }]);

    if (error) {
      console.error("タスク追加エラー:", error.message);
    } else {
      console.log("タスク追加成功:", task);
      setTask("");
    }

    setLoading(false);
  };

  return (
    <main className="p-6">
      <h1 className="text-2xl font-bold mb-4">ToDoリスト</h1>

      <form onSubmit={addTask} className="flex gap-2 mb-4">
        <input
          type="text"
          value={task}
          onChange={(e) => setTask(e.target.value)}
          placeholder="タスクを入力"
          className="border p-2 rounded w-full"
        />
        <button
          type="submit"
          disabled={loading}
          className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600 disabled:opacity-50"
        >
          {loading ? "追加中..." : "追加"}
        </button>
      </form>
    </main>
  );
}

そして、VSCodeでターミナルを開き、bashモードに切り替えて
npm run dev で立ち上げていきます。

ToDoリスト内に任意のテキストを入力をしましたが、
タスク追加エラー: Could not find the 'title' column of 'todos' in the schema cache
とエラー表示がでました。

そこで、フォーム入力からタスクを登録する際に title という
カラム名を使うコード例を書いているので、整合性を取るために
以下のカラムを追加で作成しておきます。

	•	カラム名:title
	•	型:text
	•	Not Null:✅(チェックするのが普通)
	•	Default:空(そのままでOK)

これを設定しつつ、CRUDそれぞれに設定していきます。

Supabaseでは Row Level Security (RLS) が有効になると、
デフォルトではユーザーがデータを読み書きできません。

  • insert → タスク追加が拒否される
  • select → タスク一覧の取得が拒否される

そのため、アプリから正常に操作するには
CRUDごとにポリシーを追加する必要があります。

それをふまえ、todosテーブル用 RLS ポリシーをCRUD全てに対応できるように設定します。

-- SELECT(読み取り)
create policy "Enable select for all users"
on todos
for select
to public
using (true);

-- INSERT(追加)
create policy "Enable insert for all users"
on todos
for insert
to public
with check (true);

-- UPDATE(更新)
create policy "Enable update for all users"
on todos
for update
to public
using (true)
with check (true);

-- DELETE(削除)
create policy "Enable delete for all users"
on todos
for delete
to public
using (true);

そして「npm run dev」でサーバーを起動させて、入力フォームに
「最初のタスク」でタスク名を入れて、追加ボタンを押してみます。

スクリーンショット 2025-08-20 22.03.23.png

「タスク成功」と出ていたので、supabaseをチェックします。

スクリーンショット 2025-08-20 22.06.07.png

これでsupabase上に反映されたので、C(Create)の検証が完了しました。

2. Update(更新)

続けて、タスク一覧に表示されている既存タスクの内容を編集するフェーズです。
CRUDの順で実装進めたかったのですが、今回は便宜上Updateから行います。

「最初のタスク」から「更新のタスク」へ変更します。

スクリーンショット 2025-08-20 22.09.35.png

「更新のタスク」に変わっていたので、supabaseで確認します。

スクリーンショット 2025-08-20 22.18.11.png

supabaseでも「更新のタスク」に変わっていたのが確認できました。
これでU(Update)の検証が完了しました。

3. Read(参照)

ページをリロードすると、既存タスクが正しく表示するためのフェーズです。

まずは、ブラウザの方をリロードしてみました。

スクリーンショット 2025-08-20 22.27.15.png

そして続けて、supabase上でリロードしてみます。

スクリーンショット 2025-08-20 22.30.01.png

どちらも「更新のタスク」になっていることが確認できたので、
Read(参照)の検証が完了しました。

4. DELETE(削除)

タスク一覧に表示されている既存タスクを削除する工程です。

まずは、「更新のタスク」の欄にある削除ボタンをクリックします。

スクリーンショット 2025-08-21 12.32.10.png

コンソールを確認してみると「タスク削除」の表示が見られました。
続けて、supabaseのテーブルも確認してみます。

スクリーンショット 2025-08-21 12.32.24.png

「更新のタスク」のところが削除されたのが確認できたので、
DELETE(削除)の検証が完了しました。

5. 学んだこと・つまずいた点

学んだこと:
 ・ToDoアプリにCRUDの流れを組み込む方法
 ・ UI操作しながら、supabase側のデータの状態を確認するやり方
 ・Next.js のページに 更新用のフォームや削除用のボタン を組み込む方法

つまづいた点:
 ・ Supabase クライアントファイルの配置パスのエラーの対応
 ・ Update(更新)がUIに即座に反映されるか分かりにくかった

6. まとめ

今回は 「Supabase × Next.js で ToDoリストのCURD
(Create・Update・Read・Delete)」 を実装し、
ブラウザUIと Supabase 側を照らし合わせながら処理の流れを検証しました。

エラーで止まる場面もありましたが、
 •UIから操作した内容が ブラウザ上に反映されること
 •Supabase 側のデータベースに 同じ結果が反映されていること

この両方を確認することで、
フロントエンドとバックエンドがしっかり連携していることを実感できました。

次回はさらに発展させて、
「Supabase でバックエンドを強化」
といったテーマで記事を書いていきたいと思います!

最後まで読んでいただき、ありがとうございました。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?