nextjs-supabase-saas-planner
npx skills add https://github.com/sitechfromgeorgia/georgian-distribution-system --skill nextjs-supabase-saas-planner
Agent 安装分布
Skill 文档
Next.js + Supabase SaaS Planner
Overview
Expert SaaS planning assistant that transforms product ideas into actionable technical roadmaps. Specializes in the Next.js 15 + Supabase stack, covering architecture design, database schemas, authentication patterns, multi-tenancy, billing integration, and launch strategies.
When to use this skill:
- Planning a new SaaS product from scratch
- Converting a product idea into technical specifications
- Designing database schemas for SaaS applications
- Setting up authentication and authorization
- Implementing multi-tenant architecture
- Integrating subscription billing (Stripe, Paddle)
- Planning feature rollout and MVP priorities
- Creating development roadmaps and timelines
Planning Workflow
Phase 1: Discovery and Requirements
Ask these questions first:
-
Product Overview
- What problem does your SaaS solve?
- Who is your target user?
- What’s your unique value proposition?
-
Scale Expectations
- How many users in first year? (0-1K, 1K-10K, 10K-100K, 100K+)
- Expected growth rate?
- Geographic distribution?
-
Team and Timeline
- Team size and skills?
- Launch timeline? (MVP date)
- Budget constraints?
-
Business Model
- Pricing tiers?
- Free trial or freemium?
- What features differentiate paid tiers?
-
Core Features
- Must-have features for MVP?
- Nice-to-have features for v1.1?
- Future roadmap ideas?
Phase 2: Technical Architecture Planning
Based on requirements, create:
- System Architecture Diagram
- Database Schema
- File/Folder Structure
- Authentication Flow
- Multi-Tenancy Strategy (if applicable)
- Billing Integration Plan
- Deployment Architecture
Phase 3: Development Roadmap
Create week-by-week plan:
- Week 1-2: Setup and infrastructure
- Week 3-4: Core features
- Week 5-6: Authentication and billing
- Week 7-8: Polish and testing
- Week 9: Launch preparation
Next.js 15 + Supabase Stack (2025)
Recommended Tech Stack
Frontend:
ââ Next.js 15 (App Router)
ââ React 19
ââ TypeScript
ââ Tailwind CSS
ââ shadcn/ui components
Backend:
ââ Next.js API Routes (Server Actions)
ââ Supabase Edge Functions (when needed)
ââ Stripe/Paddle webhooks
Database:
ââ PostgreSQL (Supabase)
ââ Row Level Security (RLS)
ââ Supabase Realtime (optional)
Authentication:
ââ Supabase Auth
ââ OAuth providers (Google, GitHub, etc.)
ââ Magic links
Storage:
ââ Supabase Storage (files, images)
Deployment:
ââ Vercel (frontend + API)
ââ Supabase (database, auth, storage)
ââ Cloudflare (CDN, DNS)
Payments:
ââ Stripe (recommended)
ââ Paddle (alternative)
Monitoring:
ââ Vercel Analytics
ââ Sentry (error tracking)
ââ PostHog (product analytics - optional)
Why This Stack?
Next.js 15:
- Server Components for performance
- Server Actions for mutations
- Built-in caching strategies
- Excellent DX and fast iteration
Supabase:
- Instant REST API
- Real-time subscriptions
- Built-in authentication
- Row-level security
- Open source (no vendor lock-in risk)
TypeScript:
- Type safety reduces bugs
- Better IDE support
- Self-documenting code
Stripe:
- Industry standard for payments
- Excellent documentation
- Subscription management
- Invoice handling
File Structure Template
my-saas/
ââ app/
â ââ (auth)/
â â ââ login/
â â â ââ page.tsx
â â ââ signup/
â â â ââ page.tsx
â â ââ forgot-password/
â â â ââ page.tsx
â â ââ layout.tsx
â â
â ââ (marketing)/
â â ââ page.tsx # Landing page
â â ââ pricing/
â â â ââ page.tsx
â â ââ about/
â â â ââ page.tsx
â â ââ layout.tsx
â â
â ââ (dashboard)/
â â ââ dashboard/
â â â ââ page.tsx
â â ââ settings/
â â â ââ profile/
â â â â ââ page.tsx
â â â ââ billing/
â â â â ââ page.tsx
â â â ââ team/
â â â ââ page.tsx
â â ââ layout.tsx
â â
â ââ api/
â â ââ webhooks/
â â â ââ stripe/
â â â ââ route.ts
â â ââ og/ # Open Graph images
â â ââ route.tsx
â â
â ââ layout.tsx # Root layout
â ââ globals.css
â
ââ components/
â ââ ui/ # shadcn components
â â ââ button.tsx
â â ââ card.tsx
â â ââ dialog.tsx
â â ââ ...
â ââ auth/
â â ââ login-form.tsx
â â ââ signup-form.tsx
â ââ dashboard/
â â ââ sidebar.tsx
â â ââ navbar.tsx
â ââ marketing/
â â ââ hero.tsx
â â ââ features.tsx
â â ââ pricing-cards.tsx
â ââ shared/
â ââ user-avatar.tsx
â ââ loading-spinner.tsx
â
ââ lib/
â ââ supabase/
â â ââ client.ts # Client-side Supabase
â â ââ server.ts # Server-side Supabase
â â ââ middleware.ts # Auth middleware
â ââ stripe/
â â ââ client.ts
â â ââ server.ts
â ââ db/
â â ââ queries.ts # Database queries
â ââ auth.ts # Auth helpers
â ââ utils.ts # General utilities
â
ââ hooks/
â ââ use-user.ts # User session hook
â ââ use-subscription.ts # Subscription status
â ââ use-toast.ts
â
ââ actions/ # Server Actions
â ââ auth/
â â ââ login.ts
â â ââ signup.ts
â â ââ logout.ts
â ââ billing/
â â ââ create-checkout.ts
â â ââ cancel-subscription.ts
â ââ profile/
â ââ update-profile.ts
â
ââ types/
â ââ database.ts # Generated from Supabase
â ââ supabase.ts
â ââ stripe.ts
â
ââ supabase/
â ââ migrations/ # Database migrations
â â ââ 20250101000000_initial.sql
â ââ functions/ # Edge Functions (optional)
â ââ config.toml
â
ââ public/
â ââ images/
â ââ icons/
â
ââ middleware.ts # Next.js middleware (auth)
ââ next.config.js
ââ tailwind.config.js
ââ tsconfig.json
ââ package.json
ââ .env.local
Database Schema Patterns
Core Tables for SaaS
-- Enable UUID extension
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Users table (extended from Supabase auth.users)
CREATE TABLE profiles (
id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
email TEXT UNIQUE NOT NULL,
full_name TEXT,
avatar_url TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Organizations/Teams (multi-tenant)
CREATE TABLE organizations (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name TEXT NOT NULL,
slug TEXT UNIQUE NOT NULL,
logo_url TEXT,
subscription_tier TEXT DEFAULT 'free',
subscription_status TEXT DEFAULT 'active',
stripe_customer_id TEXT UNIQUE,
stripe_subscription_id TEXT UNIQUE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Organization memberships
CREATE TABLE organization_members (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
organization_id UUID REFERENCES organizations(id) ON DELETE CASCADE,
user_id UUID REFERENCES profiles(id) ON DELETE CASCADE,
role TEXT NOT NULL DEFAULT 'member', -- 'owner', 'admin', 'member'
invited_by UUID REFERENCES profiles(id),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(organization_id, user_id)
);
-- Subscription plans
CREATE TABLE plans (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name TEXT NOT NULL,
description TEXT,
price_monthly DECIMAL(10,2),
price_yearly DECIMAL(10,2),
stripe_price_id_monthly TEXT,
stripe_price_id_yearly TEXT,
features JSONB,
limits JSONB, -- { "users": 5, "projects": 10 }
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Subscription usage tracking
CREATE TABLE usage_records (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
organization_id UUID REFERENCES organizations(id) ON DELETE CASCADE,
metric TEXT NOT NULL, -- 'api_calls', 'storage_mb', 'users'
value INTEGER NOT NULL,
period_start DATE NOT NULL,
period_end DATE NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Invitations
CREATE TABLE invitations (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
organization_id UUID REFERENCES organizations(id) ON DELETE CASCADE,
email TEXT NOT NULL,
role TEXT NOT NULL DEFAULT 'member',
token TEXT UNIQUE NOT NULL,
invited_by UUID REFERENCES profiles(id),
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
accepted_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Audit log (optional but recommended)
CREATE TABLE audit_logs (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
organization_id UUID REFERENCES organizations(id),
user_id UUID REFERENCES profiles(id),
action TEXT NOT NULL,
resource_type TEXT,
resource_id UUID,
metadata JSONB,
ip_address INET,
user_agent TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Indexes
CREATE INDEX idx_org_members_org ON organization_members(organization_id);
CREATE INDEX idx_org_members_user ON organization_members(user_id);
CREATE INDEX idx_usage_org_period ON usage_records(organization_id, period_start, period_end);
CREATE INDEX idx_invitations_token ON invitations(token);
CREATE INDEX idx_audit_logs_org ON audit_logs(organization_id);
Row Level Security (RLS) Policies
-- Enable RLS
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
ALTER TABLE organizations ENABLE ROW LEVEL SECURITY;
ALTER TABLE organization_members ENABLE ROW LEVEL SECURITY;
-- Profiles: Users can read and update their own profile
CREATE POLICY "Users can view own profile"
ON profiles FOR SELECT
USING (auth.uid() = id);
CREATE POLICY "Users can update own profile"
ON profiles FOR UPDATE
USING (auth.uid() = id);
-- Organizations: Members can read their organizations
CREATE POLICY "Members can view their organizations"
ON organizations FOR SELECT
USING (
id IN (
SELECT organization_id
FROM organization_members
WHERE user_id = auth.uid()
)
);
-- Only owners can update organizations
CREATE POLICY "Owners can update organizations"
ON organizations FOR UPDATE
USING (
id IN (
SELECT organization_id
FROM organization_members
WHERE user_id = auth.uid() AND role = 'owner'
)
);
-- Organization members: Members can view other members
CREATE POLICY "Members can view team members"
ON organization_members FOR SELECT
USING (
organization_id IN (
SELECT organization_id
FROM organization_members
WHERE user_id = auth.uid()
)
);
Authentication Setup
Supabase Auth Configuration
// lib/supabase/client.ts
import { createBrowserClient } from '@supabase/ssr';
export function createClient() {
return createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);
}
// lib/supabase/server.ts
import { createServerClient, type CookieOptions } from '@supabase/ssr';
import { cookies } from 'next/headers';
export async function createClient() {
const cookieStore = await cookies();
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
get(name: string) {
return cookieStore.get(name)?.value;
},
set(name: string, value: string, options: CookieOptions) {
cookieStore.set({ name, value, ...options });
},
remove(name: string, options: CookieOptions) {
cookieStore.set({ name, value: '', ...options });
},
},
}
);
}
Auth Middleware
// middleware.ts
import { createServerClient, type CookieOptions } from '@supabase/ssr';
import { NextResponse, type NextRequest } from 'next/server';
export async function middleware(request: NextRequest) {
let response = NextResponse.next({
request: {
headers: request.headers,
},
});
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
get(name: string) {
return request.cookies.get(name)?.value;
},
set(name: string, value: string, options: CookieOptions) {
response.cookies.set({
name,
value,
...options,
});
},
remove(name: string, options: CookieOptions) {
response.cookies.set({
name,
value: '',
...options,
});
},
},
}
);
const { data: { user } } = await supabase.auth.getUser();
// Redirect to login if not authenticated
if (!user && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url));
}
// Redirect to dashboard if already authenticated
if (user && (
request.nextUrl.pathname === '/login' ||
request.nextUrl.pathname === '/signup'
)) {
return NextResponse.redirect(new URL('/dashboard', request.url));
}
return response;
}
export const config = {
matcher: [
'/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
],
};
Stripe Integration
Setup
// lib/stripe/server.ts
import Stripe from 'stripe';
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: '2024-11-20.acacia',
});
// Create checkout session
export async function createCheckoutSession(
organizationId: string,
priceId: string,
userId: string
) {
const session = await stripe.checkout.sessions.create({
mode: 'subscription',
payment_method_types: ['card'],
line_items: [
{
price: priceId,
quantity: 1,
},
],
success_url: `${process.env.NEXT_PUBLIC_APP_URL}/dashboard/billing?success=true`,
cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/pricing`,
client_reference_id: organizationId,
metadata: {
organizationId,
userId,
},
});
return session;
}
Webhook Handler
// app/api/webhooks/stripe/route.ts
import { headers } from 'next/headers';
import { NextResponse } from 'next/server';
import Stripe from 'stripe';
import { stripe } from '@/lib/stripe/server';
import { createClient } from '@/lib/supabase/server';
export async function POST(req: Request) {
const body = await req.text();
const signature = (await headers()).get('stripe-signature')!;
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(
body,
signature,
process.env.STRIPE_WEBHOOK_SECRET!
);
} catch (err) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 400 });
}
const supabase = await createClient();
switch (event.type) {
case 'checkout.session.completed':
const session = event.data.object as Stripe.Checkout.Session;
await supabase
.from('organizations')
.update({
stripe_customer_id: session.customer as string,
stripe_subscription_id: session.subscription as string,
subscription_status: 'active',
})
.eq('id', session.metadata?.organizationId);
break;
case 'customer.subscription.updated':
const subscription = event.data.object as Stripe.Subscription;
await supabase
.from('organizations')
.update({
subscription_status: subscription.status,
})
.eq('stripe_subscription_id', subscription.id);
break;
case 'customer.subscription.deleted':
const deletedSub = event.data.object as Stripe.Subscription;
await supabase
.from('organizations')
.update({
subscription_status: 'canceled',
subscription_tier: 'free',
})
.eq('stripe_subscription_id', deletedSub.id);
break;
}
return NextResponse.json({ received: true });
}
Multi-Tenancy Patterns
Organization-Based (Recommended)
Pattern: Each organization has its own data space.
Benefits:
- Clear data isolation
- Easy to implement
- Scalable per organization
- Simple billing per organization
Implementation:
// All queries include organization_id
const { data } = await supabase
.from('projects')
.select('*')
.eq('organization_id', currentOrgId);
Subdomain-Based (Advanced)
Pattern: Each organization has its own subdomain (e.g., acme.yourapp.com).
Benefits:
- Better branding
- Clear isolation
- Professional appearance
Challenges:
- DNS management
- SSL certificates
- More complex routing
Implementation:
// middleware.ts
export function middleware(request: NextRequest) {
const hostname = request.headers.get('host')!;
const subdomain = hostname.split('.')[0];
// Fetch organization by subdomain
// Set in request context
}
MVP Feature Prioritization
Week 1-2: Foundation
- Project setup (Next.js + Supabase)
- Database schema creation
- Authentication (email + OAuth)
- Basic layouts and routing
- Landing page
Week 3-4: Core Features
- Dashboard layout
- First core feature (main value prop)
- User profile management
- Organization/team creation
- Team member invitations
Week 5-6: Monetization
- Pricing page
- Stripe integration
- Subscription management
- Usage limits enforcement
- Billing page
Week 7-8: Polish
- Email notifications
- Error handling
- Loading states
- Responsive design
- SEO optimization
Week 9: Launch Prep
- Testing (E2E, unit)
- Security audit
- Performance optimization
- Documentation
- Deployment scripts
Environment Variables
# .env.local
# Supabase
NEXT_PUBLIC_SUPABASE_URL=https://xxxxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-key
# Stripe
STRIPE_SECRET_KEY=sk_test_xxxxx
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_xxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxx
# App
NEXT_PUBLIC_APP_URL=http://localhost:3000
# Optional
SENTRY_DSN=your-sentry-dsn
POSTHOG_KEY=your-posthog-key
Deployment Checklist
Pre-Launch
- Environment variables configured
- Database migrations run
- RLS policies tested
- Stripe webhooks configured
- Domain configured
- SSL certificates active
- Analytics setup
- Error tracking (Sentry)
- Email service configured
- Terms of Service + Privacy Policy pages
Post-Launch
- Monitor error rates
- Check subscription flows
- Test webhook delivery
- Monitor database performance
- Set up backups
- Create status page
- Implement feature flags
Best Practices
Performance
- Use Server Components by default
- Implement proper caching strategies
- Optimize images with
next/image - Use React Server Actions for mutations
- Lazy load heavy components
Security
- Always use RLS policies
- Validate inputs on server
- Never expose service role key to client
- Use prepared statements (Supabase handles this)
- Implement rate limiting for sensitive endpoints
Development
- Use TypeScript strictly
- Write tests for critical paths
- Use database migrations (never manual changes)
- Version your API
- Document complex logic
User Experience
- Add loading states everywhere
- Implement optimistic updates
- Show clear error messages
- Make onboarding smooth
- Add helpful empty states
Common Patterns Reference
For detailed implementation patterns, see reference documents:
AUTHENTICATION_PATTERNS.md– OAuth, magic links, MFABILLING_PATTERNS.md– Stripe integration, webhooks, meteringMULTI_TENANT_PATTERNS.md– Organization structures, data isolationDEPLOYMENT_STRATEGIES.md– CI/CD, environments, monitoring
Example Output Format
When planning a SaaS, provide:
-
Executive Summary
- Product overview
- Target users
- Key features
-
Technical Architecture
- System diagram
- Tech stack rationale
- Database schema
-
File Structure
- Complete folder organization
- Key file descriptions
-
Development Roadmap
- Week-by-week breakdown
- Feature prioritization
- Estimated effort
-
Deployment Plan
- Infrastructure setup
- Environment configuration
- Launch checklist
-
Next Steps
- Immediate actions
- Setup commands
- Documentation links
Success Criteria
A well-planned SaaS should have:
- â Clear database schema with RLS
- â Secure authentication flow
- â Scalable architecture
- â Working billing integration
- â Organized file structure
- â Realistic timeline
- â Clear MVP scope
- â Deployment strategy