⭐ 講座10仮 — オプション

SupabaseとResendで会員管理とメール送信ができる簡易SaaSを構築できるようになる

⏱ 想定学習時間 30時間(Phase 1〜3 各10時間)
📋 前提条件 講座04 + 05 + 06修了(Vercel公開済み)
🛤 パス オプション(仮)
📖
この講座でできるようになること
  • Supabaseを使ってデータベースと会員認証を持つバックエンドを構築できる
  • 管理画面から会員情報を管理(検索・フィルター・プラン変更・CSVエクスポート)できる
  • Resendを使って自動メール送信機能(ウェルカムメール・一括送信)を実装できる
  • フロントエンド・バックエンド・メールを組み合わせた「簡易SaaS」を無料で公開できる
💰
費用について この講座で使用するすべてのサービスには無料枠があります。小規模SaaSなら月$0で運用できます。Supabase(500MB DB・50,000 MAU)、Resend(3,000通/月)、Vercel(100GB帯域)はすべて無料枠内で十分です。ユーザー数が増えてきたらSupabase Proは$25/月、Resend Proは$20/月から利用できます。
🔐
セキュリティに関する重要な注意事項 — 必ず読んでください Supabaseには2種類のキーがあります。Anon Key(公開キー)はフロントエンドから使用可能(RLS有効な場合)。Service Role Key(秘密キー)は管理者権限のためGitHubへの公開は絶対にNGです。SUPABASE_SERVICE_ROLE_KEYはVercelの環境変数にのみ設定し、.envファイルに書いてGitHubにプッシュしないでください。
完成する簡易SaaSのシステム構成
👤 ユーザー
ブラウザアクセス
▲ Vercel
React + Edge Functions
🟢 Supabase
DB + Auth + RLS
📧 Resend
メール送信
⚡ Edge Function
/api/send-email.ts

費用シミュレーション(無料枠)

サービス 無料枠 月額コスト
Supabase 500MB DB・50,000 MAU・2プロジェクト $0
Resend 100通/日・3,000通/月 $0
Vercel 100GB帯域・Hobbyプラン $0
合計 小規模SaaSなら十分 月$0
1

Phase 1: Supabaseでデータベースと認証を設定する

⏱ 約10時間
1-1

STEP 1-1: Supabaseプロジェクトの作成

SupabaseはBaaS(Backend as a Service)と呼ばれるサービスです。データベース・認証・ストレージ・Edge Functionsなどのバックエンド機能をまとめて提供しています。

  1. https://supabase.com にアクセスして「Continue with GitHub」でアカウント登録
  2. ダッシュボードで「New project」をクリック
  3. Name: my-saas-app、Database Password(強力なもの)、Region: Northeast Asia (Tokyo) を入力
  4. 「Create new project」をクリック(初期化に1〜2分かかる)
  5. 「Settings」→「API」からProject URLとAnon Keyをメモする

取得した値を .env ファイルに追記します。

env — 環境変数設定 プロジェクトルート/.env
VITE_SUPABASE_URL=https://xxxxxxxxxxxxxx.supabase.co
VITE_SUPABASE_ANON_KEY=eyJhb...(Anon Keyをここに貼り付け)
⬡ Supabase
🏠 Home
🗄 Table Editor
🔐 Authentication
📦 Storage
Edge Functions
🔑 API Keys
profiles テーブル
+ 行を追加
idemailplancreated_at
uuid-1...user1@example.comfree2026-01-01
uuid-2...user2@example.compro2026-01-02

▲ Supabase のテーブルエディタ。会員データを管理できる

⚠️ エラーが出た場合

「プロジェクトが作成できない」:無料プランではプロジェクトは2個までです。既存のプロジェクトを削除するか有料プランに移行してください。

「.envファイルが反映されない」:.envを編集したら開発サーバーを再起動してください(Ctrl+C → npm run dev)。

「GitHubに.envがプッシュされてしまった」:即座にSupabaseのダッシュボードからキーをローテーション(再発行)してください。

1-2

STEP 1-2: テーブルの作成(profiles テーブル + RLS設定)

Table Editorからprofilesテーブルを作成します。

カラム名 デフォルト値 備考
iduuidauth.uid()Primary Key
emailtextメールアドレス
nametext表示名(Nullable)
plantext'free'free / pro / premium
roletext'user'user / admin
created_attimestamptznow()登録日時

テーブル作成時に「Enable Row Level Security (RLS)」を ON にします。次にRLSポリシーを追加します。

SQL — RLSポリシー設定 Supabase SQL Editor
-- ポリシー1: 自分のプロフィールを読み取る
-- Policy name: Users can view own profile
-- Allowed operation: SELECT
auth.uid() = id

-- ポリシー2: 自分のプロフィールを更新する
-- Policy name: Users can update own profile
-- Allowed operation: UPDATE
auth.uid() = id
⚠️ エラーが出た場合

「RLSポリシーでデータが取得できない」:RLSが有効でポリシーが設定されていない場合データは取得できません。SELECTポリシーが設定されているか確認してください。

「カラムのデータ型を間違えた」:テーブルを選択して「Edit Table」から修正できます。データが入っている場合はテーブルを作り直してください。

1-3

STEP 1-3: 認証の設定(Email/Password認証 + Google AI Studioでコード生成)

  1. 「Authentication」→「Providers」→「Email」プロバイダーが ON になっていることを確認
  2. Google AI Studioで以下のプロンプトを使ってサインアップ・サインインコンポーネントを生成する
  3. 生成されたコードを src/components/Auth.tsx として保存する
Google AI Studio プロンプト — 認証コンポーネント生成
以下の条件でReact + TypeScriptのコードを作成してください。

【要件】
- Supabase の認証機能を使ったサインアップとサインインコンポーネントを作成する
- @supabase/supabase-js を使用する
- supabaseClient は ./supabaseClient からインポートする
- サインアップ: メールアドレスとパスワードでアカウント作成
- サインイン: メールアドレスとパスワードでログイン
- 認証状態に応じて表示を切り替える
- エラーメッセージを日本語で表示する
- TailwindCSSでシンプルなスタイリング

【ファイル構成】
- src/components/Auth.tsx(認証フォームコンポーネント)

完全なコードを出力してください。
⚠️ エラーが出た場合

「Email not confirmed」エラー:「Authentication」→「Providers」→「Email」で「Confirm email」をOFFにするか、メール確認のリンクをクリックしてから再サインインしてください。

「Invalid login credentials」:まずサインアップしてからサインインしているか確認してください。

「CORS エラー」:「Authentication」→「URL Configuration」の「Site URL」が正しく設定されているか確認してください。

1-4

STEP 1-4: Supabaseクライアントライブラリの設定とCRUD操作

PowerShell — パッケージインストール
PS> npm install @supabase/supabase-js
TypeScript — Supabaseクライアント設定 src/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

if (!supabaseUrl || !supabaseAnonKey) {
  throw new Error('VITE_SUPABASE_URL と VITE_SUPABASE_ANON_KEY を .env に設定してください')
}

export const supabase = createClient(supabaseUrl, supabaseAnonKey)

export type Profile = {
  id: string
  email: string
  name: string | null
  plan: 'free' | 'pro' | 'premium'
  role: 'user' | 'admin'
  created_at: string
}

主なCRUD操作

TypeScript — SELECT / INSERT / UPDATE
// SELECT: 自分のプロフィールを取得
const { data, error } = await supabase
  .from('profiles')
  .select('*')
  .eq('id', userId)
  .single()

// INSERT: プロフィールを新規作成
const { data, error } = await supabase
  .from('profiles')
  .insert({ id: userId, email, name, plan: 'free', role: 'user' })

// UPDATE: プランを更新
const { data, error } = await supabase
  .from('profiles')
  .update({ plan: 'pro' })
  .eq('id', userId)
⚠️ エラーが出た場合

「Module not found: @supabase/supabase-js」:npm install @supabase/supabase-jsを実行してください。

「Cannot read properties of undefined (reading 'VITE_SUPABASE_URL')」:.envファイルが正しい場所(package.jsonと同じフォルダ)にあるか確認してください。開発サーバーを再起動してください。

「RLS policy violation」や「permission denied」:認証済みユーザーとして操作しているか確認してください。

Phase 1 修了条件チェックリスト
  • Supabaseのアカウントを作成し、新規プロジェクトが作成できている
  • profiles テーブルが作成され、RLSが有効になっている
  • .env ファイルに VITE_SUPABASE_URLVITE_SUPABASE_ANON_KEY が設定されている
  • サインアップ・サインイン・サインアウトが動作している
  • 認証状態に応じてページの表示が切り替わっている
  • supabaseClient.ts が作成され、CRUD操作のコードが動作している
2

Phase 2: 管理画面とデータベース連携

⏱ 約10時間
2-1

STEP 2-1: 管理画面の設計と作成(管理者ロール制御)

まずSQL Editorでテスト用の管理者ユーザーのロールを変更します。

SQL — 管理者に設定 Supabase SQL Editor
UPDATE profiles
SET role = 'admin'
WHERE email = 'あなたのメールアドレス@example.com';
PowerShell — React Routerのインストール
PS> npm install react-router-dom

Google AI Studioで管理画面コンポーネントを生成します。

Google AI Studio プロンプト — 管理画面コード生成
以下の条件でReact + TypeScriptの管理画面コンポーネントを作成してください。

【要件】
- React Router v6 を使用する
- /admin パスを管理者ロールのユーザーのみアクセスできるよう保護する
- Supabase の profiles テーブルから会員一覧を取得して表示する
- 検索機能: メールアドレスまたは名前で絞り込み
- フィルター機能: プラン(free/pro/premium)で絞り込み
- ページネーション: 1ページ10件表示
- 管理者でない場合は / にリダイレクト

【ファイル】
- src/components/AdminGuard.tsx
- src/pages/AdminDashboard.tsx
- src/pages/MemberList.tsx
⚠️ エラーが出た場合

「管理者なのに /admin にアクセスできない」:SQL EditorでSELECT role FROM profiles WHERE email = 'あなたのメール'を実行してroleがadminになっているか確認してください。ブラウザのキャッシュをクリアして再ログインしてください。

「Supabase から会員一覧が取得できない」:管理者が全データを読み取れるRLSポリシーが必要です。SQL Editorで管理者向けSELECTポリシーを追加してください。

2-2

STEP 2-2: 会員管理機能(プラン変更・CSVエクスポート)

TypeScript — プラン変更関数
const updateMemberPlan = async (
  memberId: string,
  newPlan: 'free' | 'pro' | 'premium'
) => {
  const { error } = await supabase
    .from('profiles')
    .update({ plan: newPlan })
    .eq('id', memberId)

  if (error) {
    console.error('プランの更新に失敗しました:', error.message)
    return false
  }
  return true
}

CSVエクスポート機能もGoogle AI Studioで生成します。UTF-8 BOM付きで出力することでExcelでも文字化けしません。

Google AI Studio プロンプト — CSVエクスポート
以下の要件でTypeScriptのCSVエクスポート関数を作成してください。
- Supabase の profiles テーブルのデータをCSVでダウンロードする
- ファイル名: members_YYYYMMDD.csv
- 文字コード: UTF-8 BOM付き(Excelで文字化けしないため)
- ヘッダー行: ID, メールアドレス, 名前, プラン, 登録日
- ブラウザのダウンロードダイアログを表示する
⚠️ エラーが出た場合

「CSVが文字化けする」:BOM(new Uint8Array([0xEF, 0xBB, 0xBF]))をCSVデータの先頭に付与することで解決します。

「更新処理が失敗する」:RLSポリシーで管理者のUPDATE権限が設定されているか確認してください。

2-3

STEP 2-3: データ可視化ダッシュボード(recharts)

PowerShell — グラフライブラリのインストール
PS> npm install recharts
Google AI Studio プロンプト — ダッシュボード生成
以下の要件でReact + TypeScript + rechartsを使ったダッシュボードを作成してください。

【グラフ1】月別登録者数推移(LineChart)- 過去6ヶ月
【グラフ2】プラン別内訳(PieChart)- free/pro/premium の人数割合
【表】月次収益シミュレーション: free=$0, pro=$9.99, premium=$29.99で計算
- recharts の ResponsiveContainer でレスポンシブ対応
- TailwindCSS でカード形式のレイアウト
⚠️ エラーが出た場合

「グラフが表示されない・サイズが0になる」:ResponsiveContainerの親要素に高さを指定してください(例: <div style={{ height: 300 }}>)。

2-4

STEP 2-4: VercelへのデプロイとEnvironment Variables設定

  1. Vercelダッシュボードでデプロイ済みプロジェクトを開く
  2. 「Settings」→「Environment Variables」を開く
  3. VITE_SUPABASE_URLVITE_SUPABASE_ANON_KEY を追加する(Production, Preview, Development すべてに設定)
  4. 「Redeploy」ボタンで再デプロイする
  5. SupabaseダッシュボードのURL Configuration に Vercel ドメインを追加する
Vercel Environment Variables 設定値
VITE_SUPABASE_URL      = https://xxxxxx.supabase.co
VITE_SUPABASE_ANON_KEY = eyJhb...(Anon Key)
⚠️ エラーが出た場合

「本番環境でSupabaseに接続できない」:Vercelの環境変数が正しく設定されているか確認してください。変数名がVITE_で始まっているか確認してください。

「本番環境でログイン後にリダイレクトが失敗する」:SupabaseのRedirect URLsにVercelのドメインが追加されているか確認してください。

Phase 2 修了条件チェックリスト
  • /admin ルートが管理者ロールのユーザーのみアクセスできるよう保護されている
  • 会員一覧が表示され、検索・フィルタリングができる
  • 会員のプラン変更・情報編集ができる
  • 会員リストをCSV形式でエクスポートできる
  • 登録者数グラフまたはプラン別グラフが表示されている
  • Vercelの環境変数にSupabaseのキーが設定されデプロイが成功している
3

Phase 3: Resendでメール送信機能を実装する

⏱ 約10時間
3-1

STEP 3-1: Resend のセットアップ(APIキー取得)

Resendは開発者向けのメール送信APIサービスです。シンプルなAPIでHTMLメールを送信できます。無料枠は1日100通・月3,000通です。

  1. https://resend.com にアクセスしてGitHubアカウントで登録
  2. ダッシュボードの「API Keys」→「Create API Key」をクリック
  3. Name: my-saas-app、Permission: Sending access で作成
  4. 生成されたAPIキー(re_xxxxxx)をメモ(一度しか表示されない)
  5. .envに追記する(VITE_プレフィックスを付けないこと
env — Resend APIキーの設定 .env(サーバーサイド専用 — VITEプレフィックス不要)
RESEND_API_KEY=re_xxxxxx(取得したAPIキー)
⚠️
RESEND_API_KEY には VITE_ プレフィックスを付けないでください。これはVercel Edge Functionsでのみ使用します。フロントエンドに露出すると第三者にメールを悪用される危険があります。
PowerShell — resendパッケージのインストール
PS> npm install resend
⚠️ エラーが出た場合

「APIキーが無効」:APIキーが正しくコピーされているか確認してください。Resendダッシュボードで新しいAPIキーを再発行してください。

「ドメイン認証ができない」:DNSの変更が反映されるまで最大48時間かかることがあります。

3-2

STEP 3-2: Vercel Edge FunctionsでメールAPIを作成

プロジェクトのルートに api フォルダを作成し、send-email.ts を作成します。

TypeScript — メール送信API api/send-email.ts
import { Resend } from 'resend'

const resend = new Resend(process.env.RESEND_API_KEY)

export const config = { runtime: 'edge' }

export default async function handler(request: Request): Promise<Response> {
  if (request.method !== 'POST') {
    return new Response('Method Not Allowed', { status: 405 })
  }

  const { to, subject, name, type, message } = await request.json()

  const { data, error } = await resend.emails.send({
    from: 'noreply@あなたのドメイン.com',  // テスト用: onboarding@resend.dev
    to: [to],
    subject: subject,
    html: generateEmailTemplate(type, name, message),
  })

  if (error) {
    return new Response(JSON.stringify({ error: error.message }), { status: 500 })
  }

  return new Response(JSON.stringify({ success: true, id: data?.id }), { status: 200 })
}
PowerShell — テスト送信
PS> curl -X POST http://localhost:3000/api/send-email `
-H "Content-Type: application/json" `
-d '{"to":"test@example.com","subject":"テスト","name":"テストユーザー","type":"welcome"}'
→ {"success":true,"id":"email_xxxxxxxx"}

▲ Resend APIのテスト送信。{"success":true}が返ればメール送信成功

⚠️ エラーが出た場合

「RESEND_API_KEY is not defined」:Vercelの環境変数にRESEND_API_KEYが設定されているか確認してください。ローカルテストにはvercel devで起動してください。

「from アドレスが無効」:ドメイン認証が完了していない場合はonboarding@resend.devを使用してください。

「メールが届かない・迷惑メールに入る」:迷惑メールフォルダを確認してください。ドメイン認証(SPF・DKIM)が完了していることを確認してください。

3-3

STEP 3-3: ウェルカムメール・お知らせメール・一括送信の実装

1. ウェルカムメール(会員登録時自動送信)

TypeScript — サインアップ後のウェルカムメール送信 src/components/Auth.tsx(サインアップ処理に追加)
const handleSignUp = async (email: string, password: string, name: string) => {
  // 1. Supabase でアカウント作成
  const { data, error } = await supabase.auth.signUp({ email, password })
  if (error) throw error

  // 2. profiles テーブルにプロフィールを追加
  await supabase.from('profiles').insert({ id: data.user!.id, email, name, plan: 'free', role: 'user' })

  // 3. ウェルカムメールを送信
  await fetch('/api/send-email', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ to: email, subject: '【サービス名】ご登録ありがとうございます', name, type: 'welcome' })
  })
}

2. 一括メール送信(全会員へ)

⚠️
SUPABASE_SERVICE_ROLE_KEYはサーバーサイドのみで使用します。Vercelの環境変数に設定し、フロントエンドコードには絶対に含めないでください。
TypeScript — 一括メール送信API api/send-bulk-email.ts
import { Resend } from 'resend'
import { createClient } from '@supabase/supabase-js'

const resend = new Resend(process.env.RESEND_API_KEY)
const supabase = createClient(
  process.env.VITE_SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_ROLE_KEY!  // Service Role Key(サーバーサイドのみ!)
)

// 全会員を取得 → 50件ずつ分割して送信(無料枠の制限対応)
const chunkSize = 50
for (let i = 0; i < emailBatch.length; i += chunkSize) {
  await resend.batch.send(emailBatch.slice(i, i + chunkSize))
}
⚠️ エラーが出た場合

「ウェルカムメールが送信されない」:ブラウザの開発者ツール(F12)のネットワークタブで/api/send-emailのリクエストが送信されているか確認してください。

「一括送信で一部が失敗する」:Resendの無料枠(100通/日)を超えていないか確認してください。チャンクサイズを小さくしてみてください。

3-4

STEP 3-4: 完成したSaaSアプリの動作確認と公開

全機能の動作テストチェックリスト

認証機能
  • 新規アカウントでサインアップできる
  • サインアップ後にprofilesテーブルにデータが作成される
  • サインアップ後にウェルカムメールが届く
  • メールアドレス・パスワードでサインインできる
  • サインアウトできる
管理画面・メール
  • 管理者アカウントで /admin にアクセスできる
  • 通常ユーザーは /admin にアクセスできない
  • 会員一覧・検索・フィルターが動作する
  • CSVエクスポートができる
  • お知らせメールを手動送信できる

デプロイ手順

PowerShell — git push でVercelに自動デプロイ
PS> git add .
PS> git commit -m "feat: SaaSアプリ完成 - Supabase認証・管理画面・Resendメール送信"
PS> git push origin main
→ Vercelに自動デプロイ開始

よくある本番環境エラー

エラー 対処法
Invalid API keyVercelダッシュボードで環境変数を確認・再設定
CORS errorSupabaseのRedirect URLsにVercelドメインを追加
Row not foundSupabase Policiesを確認
全ページが404になるプロジェクトルートにvercel.jsonを作成(SPA設定)
JSON — vercel.json(SPA設定) プロジェクトルート/vercel.json
{
  "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
}
⚠️ エラーが出た場合

「環境変数を追加したのに反映されない」:Vercelダッシュボードで「Redeploy」→「Use existing Build Cache をOFF」にして再デプロイしてください。

「ビルドエラーが出る」:VercelのDeployments → 失敗したデプロイ → Build Logsでエラー内容を確認してください。主な原因はTypeScriptの型エラーやimportパスの誤りです。

Phase 3 修了条件チェックリスト
  • Resendのアカウントを作成しAPIキーを取得できている
  • /api/send-email.ts が作成されVercel Edge Functionとして動作している
  • 会員登録時にウェルカムメールが自動送信される
  • 管理画面からお知らせメールを手動送信できる
  • 全会員への一括メール送信が動作している
  • 本番環境での全機能の動作テストが完了している

この講座で作ったもの — 完成したSaaSアプリの機能一覧

フロントエンド
  • 認証画面(サインアップ/サインイン/サインアウト)
  • ユーザーダッシュボード
  • 管理画面 (/admin)
  • 会員一覧(検索/フィルター/ページネーション)
  • 会員編集(プラン変更)
  • CSVエクスポート
  • データ可視化グラフ
バックエンド
  • Supabase(PostgreSQL)
  • profiles テーブル
  • Row Level Security
  • Supabase Auth(認証)
  • Vercel Edge Functions
  • /api/send-email(個別送信)
  • /api/send-bulk-email(一括送信)
メール
  • Resend APIによるメール送信
  • ウェルカムメール(自動)
  • お知らせメール(手動)
  • 一括送信
  • HTMLメールテンプレート

学んだ技術スタック

カテゴリ 技術 用途
フロントエンドReact + TypeScript + ViteUI構築
データベースSupabase (PostgreSQL)データ保存
認証Supabase Authログイン管理
ホスティングVercel公開・APIサーバー
メール送信Resendトランザクションメール
コード生成Google AI Studio効率的な開発
コード編集Cursor実装・デバッグ
🚀
おめでとうございます! — 次のステップ この講座を修了することで、データベース・認証・メール送信を持つ本格的な簡易SaaSを構築・公開できるようになりました。次は Stripe(決済機能)を追加して、月額課金・プランアップグレード・インボイス自動発行ができる完全なSaaSへとステップアップしましょう!
← 講座09 有料ツール総合編 ホームに戻る →