2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

React × Supabase × Vercel で TODO アプリを作ってみた①~作ってデプロイ編~🏋️

Last updated at Posted at 2025-04-15

React × Supabase × Vercel シリーズ

ある程度、独立した内容になっていますが、ご興味あればどうぞ 💁‍♂️

はじめに

旧 Twitter 新 X を見てる感じ、個人開発をしている人は SupabaseVercel をよく使っているイメージがあります。
個人でサクッと Web アプリを作りたいなら、こいつらを採用すると良いのか? 🤔

TODO アプリを題材にして、実際に Supabase と Vercel を触ってみようのシリーズです ✊

フロントエンドは React で書きました。

Supabase, Vercel って? 🤔

どちらも 無料枠があります!

とてもうれしいですね ☺️

Supabase

Firebase の代替として注目されているオープンソースの BaaS (Backend as a Service)

PostgreSQL ベースの RDB や認証機能などが使えます。サーバレス API も作れます。

Vercel

フロントエンドの Web アプリを簡単にデプロイ・ホスティングできるプラットフォームサービス

GitHub などのバージョン管理ツールと連携して、push するだけで自動でデプロイ(CI/CD) してくれます。
プルリクエストごとに自動でプレビュー環境を作るとかもできます。

成果物 🏁

ディレクトリ構成

typescript-todo
├── README.md
└── todo-app
    ├── README.md
    ├── eslint.config.js
    ├── index.html
    ├── package-lock.json
    ├── package.json
    ├── public
    │   └── vite.svg
    ├── src
    │   ├── App.css
    │   ├── App.tsx
    │   ├── assets
    │   │   └── react.svg
    │   ├── components
    │   │   ├── CreateTodoForm
    │   │   │   ├── CreateTodoForm.css
    │   │   │   └── CreateTodoForm.tsx
    │   │   ├── TodoListItem
    │   │   │   ├── TodoListItem.css
    │   │   │   └── TodoListItem.tsx
    │   │   └── ValueViewer
    │   │       ├── ValueViewer.css
    │   │       └── ValueViewer.tsx
    │   ├── hooks
    │   │   └── useTodo.ts
    │   ├── index.css
    │   ├── lib
    │   │   └── supabaseClient.ts
    │   ├── main.tsx
    │   ├── pages
    │   │   └── TodoHome
    │   │       ├── TodoHome.css
    │   │       └── TodoHome.tsx
    │   ├── types
    │   │   └── Todo.ts
    │   ├── utils
    │   │   ├── TodoUtil.test.ts
    │   │   ├── TodoUtil.ts
    │   │   └── api.ts
    │   └── vite-env.d.ts
    ├── tsconfig.app.json
    ├── tsconfig.json
    ├── tsconfig.node.json
    ├── vite.config.ts
    └── vitest.config.ts

作り方 🔨

1. プロジェクトのセットアップ

お馴染みのおまじない 🪄
ビルドツールには Vite を使っています。

npm create vite@latest todo-app --template react-ts
cd todo-app
npm install

create-react-app は公式で非推奨となりました。
https://github.com/facebook/create-react-app/pull/17003

2. Supabase プロジェクトの作成とテーブル設計

Supabase にログインして、新しいプロジェクトを作成します 🚀

スクリーンショット 2025-04-11 10.02.44.png

テーブル設計

テーブル名: todos

カラム構成は以下の通り

カラム名 属性
id int8 Primary Key, AutoIncrement
text text not null
done bool default false
created_at timestamptz default now()
updated_at timestamptz default now()

updated_at は UPDATE の度に現在時刻で更新したいので、SQL Editor で以下のクエリを実行します。

create extension if not exists moddatetime schema extensions;

create trigger handle_updated_at before update on todos
  for each row execute procedure moddatetime (updated_at);

デフォルトで RLS(Row Level Security)が有効なはずなので、RLS Policy を作成する必要があります。

"Add RLS Policy" をポチッ

add-rls.png

"Create Policy" もポチッと押して

create-policy.png

以下のようなポリシーを追加しましょう 💁‍♂️

スクリーンショット 2025-04-11 10.24.20.png

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

Supabase と接続するのに必要なパッケージをインストール

npm install @supabase/supabase-js

開発環境用に .env.local を用意し、環境変数を定義します。

.env.local
VITE_SUPABASE_URL=https://xxx.supabase.co
VITE_SUPABASE_ANON_KEY=your-anon-key

この子たちは Settings > Data API で確認できます 👀

api-settings.png

クライアントインスタンス supabaseClient を作ります。

src/lib/supabaseClient.ts
import { createClient } from "@supabase/supabase-js";

const supabaseUrl = import.meta.env.VITE_SUPABASE_URL as string;
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY as string;

export const supabaseClient = createClient(supabaseUrl, supabaseAnonKey);

4. CRUD 処理の実装

超シンプルな CRUD です。

src/utils/todoApi.ts
import { supabaseClient } from '../lib/supabaseClient';
import { Todo } from '../types/Todo';

const supabase = supabaseClient;

export class TodoApi {
  async readItems(): Promise<Todo[]> {
    const { data, error } = await supabase
      .from('todos')
      .select('id, text, done')
      .order('id', { ascending: false });

    if (error) throw error;
    return data as Todo[];
  }

  async createItem(item: Omit<Todo, 'id'>) {
    const { error } = await supabase.from('todos').insert(item);
    if (error) throw error;
  }

  async updateItem(item: Todo) {
    const { error } = await supabase
      .from('todos')
      .update(item)
      .eq('id', item.id);
    if (error) throw error;
  }

  async deleteItem(id: number) {
    const { error } = await supabase.from('todos').delete().eq('id', id);
    if (error) throw error;
  }
}

5. React アプリの実装

割愛。リポジトリ見てください 🙇‍♂️

実装が終わったら GitHub に push しておきます。

開発サーバーは npm run dev で起動します。

6. Vercel にデプロイ

Vercel にログインして、新しいプロジェクトを作成します 🚀

以下のように設定してデプロイ!

vercel-new-project.png

ここで設定している環境変数は、本番環境用です。
開発環境と本番環境で DB を分けたい場合は、それぞれ別の Supabase プロジェクトを作ってください。

デプロイ成功しました 🎉

スクリーンショット 2025-04-11 11.29.43.png

実際、作ったアプリがデプロイされていました ✌️

スクリーンショット 2025-04-11 11.30.03.png

CI/CD

デプロイしているブランチに変更を push すると、Vercel は自動で再デプロイしてくれます。

試しに、タイトル部分を赤字にしてみます。

src/pages/TodoHome/TodoHome.tsx
<h1>Todo</h1>
src/index.css
h1 {
  font-size: 2em;
+ color: red;
}

赤くなりました!

スクリーンショット 2025-04-11 11.43.56.png

おわりに

めちゃくちゃ簡単!
Vercel では最小構成の CI/CD も提供されていて、非常に便利だなと思いました。

テストを pass したときだけデプロイしたい場合には、GitHub Actions などで CI/CD パイプラインを構築する必要があるようです。
チャレンジしてみます ✊

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?