FastAPI × SupabaseでSNSデータを安全に保存する構成を作った話

に公開

FastAPI × SupabaseでSNSデータを安全に保存する構成を作った話

Vue × FastAPIでSNSダッシュボードを作っていたとき、ふとこんな疑問が。

「あれ、このデータ、アプリ閉じたら全部消えるんじゃ……?」

最初はローカルメモリで管理していたSNSのKPIデータですが、蓄積してこそ意味がある!と思い立ち、Supabase × FastAPIでの永続化に挑戦してみました。

この記事では、

  • SupabaseとFastAPIの接続方法

  • テーブル設計とRLS(Row Level Security)対応

  • JWTとの連携ポイント

つまずいた実装トラブル

をまとめて紹介します。

なぜSupabaseを選んだのか

FirebaseよりもSQLに強く、PostgreSQL互換

RLS(Row Level Security)でユーザーごとのアクセス制限が簡単

APIキーでの連携がFastAPIと相性◎

GitHubログインなどのAuthも使える

データ構成と目的(テーブル紹介)

Supabaseで作成したテーブルは以下の通り:

カラム名 説明
id UUID 主キー、自動生成
date DATE 記録日
instagram INTEGER Instagramフォロワー数などの指標
tiktok INTEGER TikTokの指標
x INTEGER X(旧Twitter)の指標
user_id UUID ユーザー識別用のID(JWTと連携)

FastAPIとの接続実装(コード付き)

まずは .env に以下を追加:

SUPABASE_URL=https://xxxxx.supabase.co
SUPABASE_KEY=your-anon-or-service-role-key

supabase_client.py

from supabase import create_client
import os
from dotenv import load_dotenv

load_dotenv()

url = os.getenv("SUPABASE_URL")
key = os.getenv("SUPABASE_KEY")
supabase = create_client(url, key)

metrics.py(POSTの例)

from fastapi import APIRouter, Depends
from supabase_client import supabase
from dependencies import get_current_user
from datetime import date

router = APIRouter()

@router.post("/metrics")
def add_metric(data: dict, user: dict = Depends(get_current_user)):
    data["user_id"] = user["sub"]  # JWTからユーザーID取得
    data["date"] = str(date.today())
    supabase.table("metrics").insert(data).execute()
    return {"message": "Metric added"}

RLSの設定とハマりポイント

Supabaseのテーブル作成後、RLSをONにして以下のポリシーを追加:

CREATE POLICY "Allow logged-in users" ON metrics
FOR SELECT USING (auth.uid() = user_id);

よくあるハマりポイント:

supabase_key is required → .envが読み込まれていない or Keyの種類が違う(anon と service)

row violates policy → user_id を忘れている or JWTのsubと一致していない

おわりに

今までは「とりあえず見える化」で満足していたダッシュボードも、Supabaseで蓄積できるようになって初めて、「成長を追えるツール」に育ちました。

次回はこのデータを Vue + Chart.js でグラフ表示し、
より直感的なKPI分析ができるダッシュボードに進化させていく予定です💪

📦 完成したコードはこちら👇🔗 GitHub - TKNRFJK1208/kpi-dashboard

🙌 最後まで読んでいただきありがとうございます!
質問や感想はZennのコメントでもTwitterでもお待ちしてます〜!

Discussion