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?

Lovableでバイブコーディングする時の注意点~EdgeFunction編

Posted at

Lovableでバイブコーディングする時の注意点~EdgeFunction編~

目次

1. 背景(Lovable×SupabaseとEFの自動増殖)
2. Supabase Edge Functionの立ち位置
3. 設計の境界線を先に引く
4. なぜ複雑なロジックをEFに置かないのか
5. 失敗パターンの匂い
6. 実装リファレンス-DBに寄せる薄いEF
7. Lovable×EF運用の注意点
8. いつEFを使う?使わない?
9. まとめ


1. 背景(Lovable×SupabaseとEFの自動増殖)

Lovable を使ってバイブコーディングしていると、Supabase 連携の流れで Edge Function(以下 EF) が「いつの間にか」生成・更新されることがあります。
気づいたら複数の EF ができていたり、別の修正の巻き添えで 意図しないタイミングで EF が更新され、既存機能が壊れる —— というのは現場あるあるです。

本記事の主張はシンプルです。
複雑な業務ロジックは EF に置かず、EF は“薄い入口(オーケストレーション)”に徹する。
そのうえで、Lovable×Supabase での設計境界と実装パターンを具体化します。


2. Supabase Edge Functionの立ち位置

  • Deno ランタイム上の TypeScript 関数を、Supabase がサーバレスで配信する仕組みです。
  • 典型用途は HTTP 入口:Webhook 受信、外部 API との仲介、軽い入力正規化、DB 関数(RPC) 呼び出しなど。
  • サーバレス特有の 実行時間・リソース制限 があるため、長時間・重処理・多段フローには向きません。
  • 認可や整合性、一貫したトランザクションは DB 側(RLS/ポリシー、SQL/PLpgSQL 関数) に寄せるのがセオリーです。

3. 設計の境界線を先に引く

レイヤ 役割 具体例
フロント(Lovable生成コード) 表示・入力・軽い前処理。秘密情報は持たない フォーム検証、楽観 UI、キャッシュ
RLS/ポリシー(認可) 誰が何に触れるかを強制 マルチテナントでのテーブルアクセス制御
DB関数(RPC) トランザクション・整合性・集約 注文作成、在庫引当、監査ログの一貫更新
Edge Function 薄いオーケストレーション JWT/署名検証、入力整形、外部 API 連携、RPC 1〜2 回

目安:「DB の中で完結する話」= DB 関数/RLS「外界と話す入口」= EF


4. なぜ複雑なロジックをEFに置かないのか

  1. 運用リスク(意図せぬ上書き)
    EF のコードが Lovable プロジェクト配下にあると、自動生成や別修正の影響で 差分が混入 しやすく、複雑な業務ルールほど デグレの温床 になります。

  2. スケーリング適性のミスマッチ
    サーバレスには実行時間やリソースの上限があります。多段集約・重い検証・外部 API 多発などの“太い”処理は タイムアウトや不安定化 を招きがちです。

  3. 責務の分散による見通し低下
    認可・整合性・一貫性は RLS/DB 関数 の責務です。EF に積むほどロジックが分散し、レビュー・テスト・監査 が難しくなります。


5. 失敗パターンの匂い

  • 業務フローを EF にベタ書きiffor で長大化)。
  • 認可(誰が何に触れるか)を フロントや EF の if に閉じ込める。
  • Webhook で 署名検証なし に DB を直更新。
  • EF が 3〜5 本に分裂し、どれが真の業務ルールか不明

6. 実装リファレンス-DBに寄せる薄いEF

6.1 DB 関数(RPC)に“芯”を寄せる

-- db/functions/orders_create.sql
create or replace function public.orders_create(
  p_user uuid,
  p_payload jsonb
) returns uuid
language plpgsql
as $$
declare
  v_order_id uuid;
begin
  -- RLS/制約の下、整合性チェックと一貫更新を実施
  insert into orders(user_id, payload)
  values (p_user, p_payload)
  returning id into v_order_id;

  -- 例: 在庫引当や監査ログなどを同一トランザクションで確定
  -- perform inventory_reserve(v_order_id, ...);
  -- insert into audit_logs(...);

  return v_order_id;
end;
$$ security definer;  -- 権限設計は要件に応じて
  • 整合性・一貫性を DB 側に閉じ込めるのがポイントです。
  • RLS/ポリシーで “誰がこの RPC を呼べるか” を制御します。

6.2 EF は「入口+薄い仲介」に徹する

// supabase/functions/create-order/index.ts
// Deno.serve で EF を公開。JWT 検証→入力整形→RPC 1回→結果返却の薄い流れ。
import { createClient } from "npm:@supabase/supabase-js";

Deno.serve(async (req) => {
  try {
    // 認証ヘッダをそのまま内部クライアントに引き継ぐ
    const auth = req.headers.get("authorization") ?? "";

    const supabase = createClient(
      Deno.env.get("SUPABASE_URL")!,
      Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!, // 秘密は環境変数で管理
      { global: { headers: { Authorization: auth } } }
    );

    const { data: userData, error: userErr } = await supabase.auth.getUser();
    if (userErr || !userData?.user) {
      return new Response("Unauthorized", { status: 401 });
    }

    const body = await req.json();
    // 必要最小の入力検証のみ(詳細はDB関数へ委譲)
    if (typeof body !== "object") {
      return new Response("Bad Request", { status: 400 });
    }

    const { data, error } = await supabase.rpc("orders_create", {
      p_user: userData.user.id,
      p_payload: body,
    });
    if (error) {
      return new Response(error.message, { status: 400 });
    }

    return new Response(JSON.stringify({ orderId: data }), {
      headers: { "content-type": "application/json" },
    });
  } catch (e) {
    return new Response("Internal Error", { status: 500 });
  }
});
  • EF 内で業務ロジックを書かない
  • 入力は 最低限の型・形式チェック に留め、詳細検証は RPC 側で行います。

6.3 フロントからの呼び出し(Lovable 生成コードの最小例)

// src/lib/api.ts
import { createClient } from "@supabase/supabase-js";
const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);

export async function createOrder(payload: unknown) {
  const { data, error } = await supabase.functions.invoke("create-order", {
    body: payload,
  });
  if (error) throw error;
  return data as { orderId: string };
}
  • 秘密鍵はフロントに置かないanon キーのみ)。
  • EF 側は service_role を使用しつつ、RLS と RPC 権限設計で安全に仲介する前提です。

7. Lovable×EF運用の注意点

  • 業務は DB に寄せる(最重要)。EF を薄く保つほど、意図せぬ上書きの影響が小さくなります。
  • 複雑な業務は 別API に寄せる(最重要)。自作の外部APIに複雑な業務を任せます。
  • ディレクトリ分離/supabase/functions をサブパッケージ化し、Lovable の生成コードとレビュールールを分けます。
  • CODEOWNERS/CI ガード:EF 配下の差分はレビュー必須、supabase functions deploy は CI のみ許可。
  • Webhook は署名検証を先に:公開 EF を使うなら外部サービスの署名検証を通し、DB 更新は RPC に一本化。

8. いつEFを使う?使わない?

使う(◯)

  • 外部 Webhook の受信(例:決済、メール)
  • 外部 API 連携の入口(署名付与、最小の前処理)
  • RLS で直接は難しい ごく小さな仲介 → RPC 1〜2 回

使わない(×)

  • 長時間・多段の集約や重い検証
  • 認可やテナント分離(RLS/ポリシーへ)
  • トランザクションを跨ぐ複雑な一貫更新(DB 関数へ)

9. まとめ

  • EF は“薄い入口”業務の芯は DB 側:これが壊れにくく、速い構成です。
  • Lovable の自動更新から守るには、EF を最小化し、ディレクトリ分離+CI ガードを徹底します。
  • 設計は フロント/RLS/DB 関数/EF の責務を最初に引き、EF に複雑さを持ち込まないこと。

この原則を守れば、Lovable×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?