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