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に置かないのか
-
運用リスク(意図せぬ上書き)
EF のコードが Lovable プロジェクト配下にあると、自動生成や別修正の影響で 差分が混入 しやすく、複雑な業務ルールほど デグレの温床 になります。 -
スケーリング適性のミスマッチ
サーバレスには実行時間やリソースの上限があります。多段集約・重い検証・外部 API 多発などの“太い”処理は タイムアウトや不安定化 を招きがちです。 -
責務の分散による見通し低下
認可・整合性・一貫性は RLS/DB 関数 の責務です。EF に積むほどロジックが分散し、レビュー・テスト・監査 が難しくなります。
5. 失敗パターンの匂い
- 業務フローを EF にベタ書き(
if
とfor
で長大化)。 - 認可(誰が何に触れるか)を フロントや 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 の“爆速”を保ったまま、デグレに強い境界を作れます。