ANTIGRAVITY LABEN
記事一覧/アプリ開発
アプリ開発/2026-04-28上級

Antigravity で SaaS を丸ごと作る — Stripe 課金・認証・デプロイを1つのIDEで完結させる

Antigravity IDE だけで SaaS プロダクトを構築する全工程を解説します。Supabase認証、Stripe課金、Cloudflare Workersデプロイまで、IDEを離れずに完結させる実践ガイドです。

antigravity345saas10stripe9supabase7cloudflare-workers7monetization10fullstack5deployment5

SaaS を作ろうとすると、ツールの数に圧倒されます。IDE、ターミナル、ブラウザ、Stripe ダッシュボード、デプロイツール……。往復するたびに集中力が削がれ、「どのツールで何をやっていたのか」を思い出すのに数分かかることもあります。

Antigravity を使い始めてから、この往復がほぼなくなりました。IDE の中だけで、認証からデータベース、課金、デプロイまで完結します。AI エージェントがコードを書き、ターミナルでテストし、そのまま本番にデプロイできます。開発体験が大きく変わります。

このガイドでは、Antigravity だけを使って、会員機能付きの SaaS を 1 つ作ります。読み終えたときに、あなたも「IDE から出る必要はないな」と感じるはずです。

SaaS のアーキテクチャ全体像

完成する SaaS は、こんな構成です。

  • フロントエンド: Next.js App Router(Antigravity で作成)
  • 認証: Supabase Auth(メール / Google OAuth)
  • データベース: PostgreSQL in Supabase(RLS で行レベルセキュリティ)
  • 決済: Stripe Checkout + Webhook
  • API: Next.js Route Handlers + Supabase
  • デプロイ先: Cloudflare Workers(Vercel 併用も可)

この構成の利点は、すべてが Antigravity の AI エージェントで操作できる点です。ダッシュボードを開かずに済みます。

Antigravity でプロジェクトを初期化する

Antigravity を開き、新しいプロジェクトを作成します。テンプレートは「Next.js with TypeScript」を選びます。

プロジェクトが開いたら、Antigravity のエージェント(AIアシスタント)に以下のプロンプトを投げます。

次のパッケージを package.json に追加してください:
- @supabase/supabase-js
- @supabase/auth-helpers-nextjs
- stripe
- clsx

その後 npm install を実行してください。

エージェントが自動で依存を追加し、インストールを完了します。ターミナルを開く必要はありません。

次に、環境変数を設定します。Antigravity の「Environment」タブで、以下を追加します。

NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOi...(Supabase から取得)
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOi...(同じく Supabase)
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...(Stripe)
STRIPE_SECRET_KEY=sk_live_...(同じく Stripe)
NEXT_PUBLIC_APP_URL=http://localhost:3000(本番は実際の URL)

認証を Supabase で実装する

Supabase の認証システムを使うことで、パスワード管理・OTP・OAuth をすべて Supabase に任せられます。

Antigravity のエージェントに、こう指示します。

lib/supabase.ts を作成してください。内容は以下です:

import { createClient } from '@supabase/supabase-js';

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!;

export const supabase = createClient(supabaseUrl, supabaseAnonKey);

export async function getUser() {
  const { data: { user } } = await supabase.auth.getUser();
  return user;
}

次に、ログインページを作成します。

app/(auth)/login/page.tsx を作成してください。以下の機能を含めます:

1. メールアドレス入力フォーム
2. "Sign in with Email" ボタン(Supabase Auth を使用)
3. "Sign in with Google" ボタン(OAuth)
4. サインアップリンク

TypeScript を使い、エラーハンドリングも含めてください。

エージェントが React コンポーネントを生成します。コンポーネントの例:

'use client';
 
import { useState } from 'react';
import { supabase } from '@/lib/supabase';
 
export default function LoginPage() {
  const [email, setEmail] = useState('');
  const [loading, setLoading] = useState(false);
 
  async function handleEmailSignIn(e: React.FormEvent) {
    e.preventDefault();
    setLoading(true);
 
    const { error } = await supabase.auth.signInWithPassword({
      email,
      password: '', // Magic link flow の場合は不要
    });
 
    if (error) {
      console.error('Sign-in error:', error.message);
    } else {
      // ダッシュボードにリダイレクト
      window.location.href = '/dashboard';
    }
 
    setLoading(false);
  }
 
  return (
    <div className="max-w-md mx-auto mt-10">
      <h1 className="text-2xl font-bold mb-6">Sign In</h1>
      <form onSubmit={handleEmailSignIn}>
        <input
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          placeholder="your@email.com"
          required
          className="w-full px-4 py-2 border rounded-lg mb-4"
        />
        <button
          type="submit"
          disabled={loading}
          className="w-full bg-blue-600 text-white py-2 rounded-lg"
        >
          {loading ? 'Signing in...' : 'Sign in with Email'}
        </button>
      </form>
    </div>
  );
}

データベース設計:RLS で安全に

Supabase のダッシュボードで、テーブルを 2 つ作成します。

users テーブル (Supabase Auth と連動)

- id (UUID, PK)
- email (text)
- created_at (timestamp)
- subscription_status (text: 'free', 'pro', 'premium')
- stripe_customer_id (text, unique)

articles テーブル (プレミアム記事)

- id (UUID, PK)
- user_id (UUID, FK → users.id)
- title (text)
- content (text)
- created_at (timestamp)
- is_premium (boolean)

最も重要な部分は Row Level Security (RLS) です。有効にします。

articles テーブルの RLS ポリシー:

-- ユーザーは自分の記事だけを見られる
CREATE POLICY "Users can view their own articles"
ON articles
FOR SELECT
USING (auth.uid() = user_id);
 
-- ユーザーは自分の記事だけを作成・更新できる
CREATE POLICY "Users can insert their own articles"
ON articles
FOR INSERT
WITH CHECK (auth.uid() = user_id);

Antigravity のエージェントに以下を指示:

lib/database.ts を作成してください。
以下の関数を含めます:

- fetchUserArticles(userId): 指定ユーザーの記事をすべて取得
- createArticle(userId, title, content, isPremium): 記事を作成
- deleteArticle(articleId, userId): 記事を削除(自分のものだけ)

Supabase クライアントを使い、エラーハンドリングを含めてください。

生成されるコード例:

import { supabase } from './supabase';
 
export async function fetchUserArticles(userId: string) {
  const { data, error } = await supabase
    .from('articles')
    .select('*')
    .eq('user_id', userId)
    .order('created_at', { ascending: false });
 
  if (error) throw new Error(error.message);
  return data;
}
 
export async function createArticle(
  userId: string,
  title: string,
  content: string,
  isPremium: boolean
) {
  const { data, error } = await supabase
    .from('articles')
    .insert([{
      user_id: userId,
      title,
      content,
      is_premium: isPremium,
    }])
    .select();
 
  if (error) throw new Error(error.message);
  return data[0];
}

Stripe Checkout と Webhook の実装

Stripe の課金フローは 3 ステップです。

  1. Checkout セッション作成 → ユーザーをチェックアウト画面に送る
  2. 決済完了 → Stripe が webhook を送る
  3. subscription_status を更新 → ユーザーのプランを 'pro' に変更

まず、API ルートを作成します。Antigravity のエージェントに:

app/api/create-checkout-session/route.ts を作成してください。

以下の処理を含めます:
1. リクエストボディから userId を取得
2. Stripe の Checkout Session を作成
3.成功時は sessionId を返す
4. エラー時は 500 を返す

Stripe の priceId は環境変数から読み込みます。

生成されるコード例:

import Stripe from 'stripe';
import { NextRequest, NextResponse } from 'next/server';
 
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
 
export async function POST(req: NextRequest) {
  const { userId, email } = await req.json();
 
  try {
    const session = await stripe.checkout.sessions.create({
      payment_method_types: ['card'],
      mode: 'subscription',
      line_items: [
        {
          price: process.env.NEXT_PUBLIC_STRIPE_PRICE_PRO!, // 'price_xxx'
          quantity: 1,
        },
      ],
      success_url: `${process.env.NEXT_PUBLIC_APP_URL}/dashboard?session_id={CHECKOUT_SESSION_ID}`,
      cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/pricing`,
      customer_email: email,
      metadata: {
        userId,
      },
    });
 
    return NextResponse.json({ sessionId: session.id });
  } catch (error: any) {
    return NextResponse.json(
      { error: error.message },
      { status: 500 }
    );
  }
}

次に、Webhook ハンドラーを作成します。

app/api/webhooks/stripe/route.ts を作成してください。

以下の処理を含めます:
1. Stripe からのリクエストを署名検証で確認
2. イベントタイプ 'customer.subscription.updated' と 'customer.subscription.created' を処理
3. metadata.userId を取得し、Supabase の users テーブルの subscription_status を 'pro' に更新
4. エラーハンドリング

webhook secret は環境変数から読み込みます。

生成されるコード例:

import Stripe from 'stripe';
import { NextRequest, NextResponse } from 'next/server';
import { supabase } from '@/lib/supabase';
 
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!;
 
export async function POST(req: NextRequest) {
  const body = await req.text();
  const sig = req.headers.get('stripe-signature')!;
 
  let event: Stripe.Event;
 
  try {
    event = stripe.webhooks.constructEvent(body, sig, webhookSecret);
  } catch (err: any) {
    return NextResponse.json(
      { error: `Webhook signature verification failed: ${err.message}` },
      { status: 400 }
    );
  }
 
  if (
    event.type === 'customer.subscription.created' ||
    event.type === 'customer.subscription.updated'
  ) {
    const subscription = event.data.object as Stripe.Subscription;
    const userId = subscription.metadata.userId;
 
    const { error } = await supabase
      .from('users')
      .update({ subscription_status: 'pro' })
      .eq('id', userId);
 
    if (error) {
      console.error('Failed to update user subscription:', error.message);
      return NextResponse.json({ error: error.message }, { status: 500 });
    }
  }
 
  return NextResponse.json({ received: true });
}

Antigravity のターミナルで、webhook をローカルテストできます。Stripe CLI を使用:

stripe listen --forward-to localhost:3000/api/webhooks/stripe
stripe trigger customer.subscription.created

エージェントに指示して、テスト用のスクリプトを生成させることもできます。

フロントエンド:ダッシュボードの構築

ログイン後のダッシュボードを作成します。

app/(authenticated)/dashboard/page.tsx を作成してください。

以下の機能を含めます:
1. 現在のユーザーを取得(Supabase Auth)
2. ユーザーの subscription_status を表示
3. 自分の記事一覧を表示(articles テーブルから)
4. "記事を書く" ボタン
5. "Pro にアップグレード" ボタン(subscription_status が 'free' の場合のみ)

エラーハンドリング・ローディング状態も含めてください。

生成されるコンポーネント例:

'use client';
 
import { useEffect, useState } from 'react';
import { supabase } from '@/lib/supabase';
import Link from 'next/link';
 
interface User {
  id: string;
  email: string;
  subscription_status: string;
}
 
interface Article {
  id: string;
  title: string;
  created_at: string;
}
 
export default function DashboardPage() {
  const [user, setUser] = useState<User | null>(null);
  const [articles, setArticles] = useState<Article[]>([]);
  const [loading, setLoading] = useState(true);
 
  useEffect(() => {
    async function loadData() {
      const { data: { user: authUser } } = await supabase.auth.getUser();
 
      if (!authUser) {
        window.location.href = '/login';
        return;
      }
 
      const { data: userData } = await supabase
        .from('users')
        .select('*')
        .eq('id', authUser.id)
        .single();
 
      const { data: articlesData } = await supabase
        .from('articles')
        .select('id, title, created_at')
        .eq('user_id', authUser.id)
        .order('created_at', { ascending: false });
 
      setUser(userData);
      setArticles(articlesData || []);
      setLoading(false);
    }
 
    loadData();
  }, []);
 
  if (loading) return <div>Loading...</div>;
 
  return (
    <div className="max-w-4xl mx-auto p-6">
      <h1 className="text-3xl font-bold mb-4">Dashboard</h1>
 
      <div className="bg-gray-100 p-4 rounded-lg mb-6">
        <p className="text-lg">
          Status: <strong>{user?.subscription_status}</strong>
        </p>
        {user?.subscription_status === 'free' && (
          <button
            onClick={() => {
              // Stripe Checkout に遷移
              fetch('/api/create-checkout-session', {
                method: 'POST',
                body: JSON.stringify({
                  userId: user.id,
                  email: user.email,
                }),
              })
                .then((res) => res.json())
                .then((data) => {
                  window.location.href = `https://checkout.stripe.com/pay/${data.sessionId}`;
                });
            }}
            className="mt-4 bg-blue-600 text-white px-4 py-2 rounded"
          >
            Upgrade to Pro
          </button>
        )}
      </div>
 
      <h2 className="text-2xl font-bold mb-4">Your Articles</h2>
      {articles.length === 0 ? (
        <p>No articles yet.</p>
      ) : (
        <ul className="space-y-2">
          {articles.map((article) => (
            <li key={article.id} className="border-b pb-2">
              <Link href={`/articles/${article.id}`} className="text-blue-600">
                {article.title}
              </Link>
              <p className="text-sm text-gray-500">
                {new Date(article.created_at).toLocaleDateString()}
              </p>
            </li>
          ))}
        </ul>
      )}
 
      <Link href="/articles/new" className="mt-6 inline-block bg-green-600 text-white px-4 py-2 rounded">
        Write New Article
      </Link>
    </div>
  );
}

Cloudflare Workers へのデプロイ

Antigravity のターミナルで、以下のコマンドを実行します。

npm install -g wrangler
wrangler login

プロジェクトルートに wrangler.toml を作成します。エージェントに指示:

wrangler.toml を作成してください。内容は以下です:

name = "my-saas-app"
type = "javascript"
compatibility_date = "2024-01-01"

[env.production]
routes = [
  { pattern = "example.com/*", zone_name = "example.com" }
]

[[triggers.crons]]
cron = "0 * * * *"

[build]
command = "npm install && npm run build"
cwd = "./"

その後、デプロイします。

wrangler deploy

Antigravity のターミナルからコマンドを実行するだけで、Cloudflare に本番アプリがデプロイされます。

カスタムドメインを設定する場合も、ターミナルから:

wrangler domains create my-saas-app.com --environment production

IDE を出ることなく、すべてが完了します。

本番環境でのテスト

デプロイ後、本番環境で一連の操作をテストします。

  1. 本番 URL でサインアップ
  2. 記事を 1 つ作成
  3. Pro にアップグレード(テスト用 Stripe カードを使用)
  4. webhook が正しく処理されたか確認

Antigravity のエージェントに、テスト用の E2E テストスクリプトを生成させることもできます。

tests/e2e.test.ts を作成してください。

以下のシナリオをテストします:
1. ユーザーがサインアップできる
2. 記事を作成できる
3. Stripe Checkout セッションが作成される
4. Webhook で subscription_status が更新される

Playwright を使用してください。

IDE を出ない開発の価値

SaaS 開発は複雑です。通常なら、IDE・ブラウザ・ターミナル・Stripe ダッシュボード・Supabase コンソール……と、複数のツールを行き来することになります。

Antigravity を使えば、IDE だけで完結します。コードを書き、AI エージェントが実装し、ターミナルでテストし、そのままデプロイできます。認知的負荷が大きく減ります。

さらに、AI エージェントの提案を受けながら開発できるので、ベストプラクティスに自然と従うようになります。「このフローでいいのか?」と迷うとき、エージェントに聞けば、プロの実装が返ってくる。

このガイドで作ったアーキテクチャは、小規模な SaaS だけでなく、スケールする本番環境にも対応しています。RLS でセキュリティを、Stripe Webhook でビジネスロジックを、Cloudflare Workers で高速なグローバルデプロイを実現できます。

あなたの次のプロダクトは、Antigravity だけで作ってみてください。その開発体験の違いに驚くと思います。

シェア

お読みいただきありがとうございます

Antigravity Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

アプリ開発2026-04-06
Antigravity × マルチテナント SaaS 実装ガイド:Supabase RLS × Stripe メーター課金 × RBAC 権限制御
Antigravity を使ってマルチテナント SaaS を本番レベルで構築する完全ガイド。Supabase の Row Level Security でテナント分離、Stripe メーター課金で従量制プラン、RBAC で権限制御を実装する設計パターンと実装コードを詳解します。
Tips & 活用術2026-04-06
AntigravityでSaaS MVP開発: アイデアから初収益まで90日完全ロードマップ
Antigravityを活用してSaaS MVPを90日で初収益化する完全ロードマップ。アイデア検証から技術スタック選定、コア機能実装、Stripe課金導入、初回ユーザー獲得まで、個人開発者が実践できる具体的なステップを網羅します。
アプリ開発2026-04-27
AntigravityでWebhook受信エンドポイントを取りこぼしなく作る実践パターン
StripeやGitHubからのWebhookを確実に処理するエンドポイントの設計・実装・テストを、Antigravity を使って効率的に進めるための実践パターンをまとめました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →