Skip to main content

SaaS Starter Example

Build a complete SaaS application with authentication, subscriptions, and team management.

Overview

This example creates a production-ready SaaS with:

  • User authentication (JWT)
  • Stripe subscriptions
  • Team/organization support
  • Admin dashboard
  • User settings
  • Email notifications

Create the Project

npx autodeploybase init my-saas \
--framework next \
--archetype saas \
--database postgresql \
--plugins payments-stripe,email

cd my-saas

Project Structure

my-saas/
├── prisma/
│ └── schema.prisma
├── src/
│ ├── app/
│ │ ├── (auth)/
│ │ │ ├── login/
│ │ │ └── register/
│ │ ├── (dashboard)/
│ │ │ ├── dashboard/
│ │ │ ├── settings/
│ │ │ └── billing/
│ │ ├── admin/
│ │ │ ├── users/
│ │ │ └── settings/
│ │ └── api/
│ │ ├── auth/
│ │ ├── billing/
│ │ └── webhooks/
│ ├── components/
│ └── lib/
├── .env.example
└── autodeploy.config.json

Configuration

Environment Variables

.env
# Database
DATABASE_URL="postgresql://postgres:password@localhost:5432/my_saas"

# Auth
JWT_SECRET="your-secure-secret-at-least-32-chars"

# Stripe
STRIPE_SECRET_KEY="sk_test_..."
STRIPE_PUBLISHABLE_KEY="pk_test_..."
STRIPE_WEBHOOK_SECRET="whsec_..."

# Email
EMAIL_PROVIDER="resend"
RESEND_API_KEY="re_..."

# App
NEXT_PUBLIC_APP_URL="http://localhost:3000"

Stripe Products

Create products in Stripe Dashboard:

  1. Starter - $9/month
  2. Pro - $29/month
  3. Enterprise - $99/month

Add price IDs to config:

autodeploy.config.json
{
"plugins": {
"payments-stripe": {
"settings": {
"products": [
{ "name": "Starter", "priceId": "price_xxx" },
{ "name": "Pro", "priceId": "price_yyy" },
{ "name": "Enterprise", "priceId": "price_zzz" }
]
}
}
}
}

Setup

# Install dependencies
npm install

# Set up database
npx prisma generate
npx prisma migrate dev --name init
npx prisma db seed

# Start development
npm run dev

Features Walkthrough

1. User Registration

Users can register with email and password:

app/(auth)/register/page.tsx
import { RegisterForm } from '@/components/auth/RegisterForm';

export default function RegisterPage() {
return (
<div className="auth-page">
<h1>Create Account</h1>
<RegisterForm />
</div>
);
}

2. Subscription Management

Users can upgrade/downgrade their plan:

app/(dashboard)/billing/page.tsx
import { PricingTable } from '@/components/billing/PricingTable';
import { CurrentPlan } from '@/components/billing/CurrentPlan';
import { getUser } from '@/lib/auth';

export default async function BillingPage() {
const user = await getUser();

return (
<div>
<h1>Billing</h1>
<CurrentPlan subscription={user.subscription} />
<PricingTable currentPlan={user.subscription?.plan} />
</div>
);
}

3. Team Management

Pro and Enterprise users can create teams:

app/(dashboard)/team/page.tsx
import { TeamMembers } from '@/components/team/TeamMembers';
import { InviteForm } from '@/components/team/InviteForm';

export default async function TeamPage() {
return (
<div>
<h1>Team</h1>
<InviteForm />
<TeamMembers />
</div>
);
}

4. Admin Dashboard

Admins can manage users and settings:

app/admin/page.tsx
import { AdminStats } from '@/components/admin/AdminStats';
import { RecentUsers } from '@/components/admin/RecentUsers';
import { RevenueChart } from '@/components/admin/RevenueChart';

export default async function AdminDashboard() {
return (
<div className="grid gap-6">
<AdminStats />
<div className="grid grid-cols-2 gap-6">
<RecentUsers />
<RevenueChart />
</div>
</div>
);
}

Database Schema

prisma/schema.prisma
model User {
id String @id @default(cuid())
email String @unique
password String
name String?
role Role @default(USER)
subscription Subscription?
team Team? @relation("TeamMembers")
teamId String?
ownedTeam Team? @relation("TeamOwner")
createdAt DateTime @default(now())
}

model Subscription {
id String @id @default(cuid())
userId String @unique
user User @relation(fields: [userId], references: [id])
stripeCustomerId String @unique
stripePriceId String
status SubscriptionStatus
currentPeriodEnd DateTime
}

model Team {
id String @id @default(cuid())
name String
ownerId String @unique
owner User @relation("TeamOwner", fields: [ownerId], references: [id])
members User[] @relation("TeamMembers")
}

Customization

Add Features

# Add analytics
npx autodeploybase add analytics

# Add file uploads
npx autodeploybase add file-storage

Customize Theme

app/globals.css
:root {
--primary: #6366f1;
--primary-foreground: #ffffff;
--background: #ffffff;
--foreground: #1a1a1a;
}

.dark {
--background: #1a1a1a;
--foreground: #ffffff;
}

Add Pages

app/(dashboard)/analytics/page.tsx
export default function AnalyticsPage() {
return (
<div>
<h1>Analytics</h1>
{/* Your analytics content */}
</div>
);
}

Deployment

Vercel

vercel

Set environment variables in Vercel dashboard.

Docker

docker-compose up -d

Next Steps

  1. Configure Stripe webhooks for subscription events
  2. Add custom pricing page with your plans
  3. Implement team features for collaboration
  4. Add email templates for notifications
  5. Set up monitoring with your preferred tool