Skip to content

Tutorial 5: Building a SaaS with MakerKit

Commercial License Required

MakerKit requires a commercial license. While Orchestre can help you build with it, you must obtain a valid license from MakerKit for commercial use.

In this comprehensive tutorial, you'll build a complete SaaS application using the MakerKit template. We'll create a project management tool with teams, subscriptions, and real features that you could actually ship.

Learning Objectives

By the end of this tutorial, you'll:

  • ✅ Build a production-ready SaaS
  • ✅ Implement multi-tenant architecture
  • ✅ Set up subscription billing
  • ✅ Add team collaboration features
  • ✅ Deploy to production

Prerequisites

  • Completed beginner tutorials
  • Basic Next.js knowledge
  • Stripe account (free)
  • 2-3 hours of time

Project Overview

We're building TaskFlow - a team project management SaaS with:

  • User authentication
  • Team workspaces
  • Project and task management
  • Subscription tiers
  • Admin dashboard

Part 1: Project Setup

Initialize the Project

bash
/orchestre:create (MCP) taskflow makerkit-nextjs

Configure Environment

bash
cd taskflow
cp .env.example .env.local

Update .env.local:

bash
# Database (use local PostgreSQL or Supabase)
DATABASE_URL=postgresql://...

# Authentication
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your-secret-here

# Stripe (from your Stripe dashboard)
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
STRIPE_PUBLISHABLE_KEY=pk_test_...

Initial Orchestration

bash
/orchestre:orchestrate (MCP) "Build a project management SaaS where teams can create projects, manage tasks, and collaborate. Include three subscription tiers: Free (1 project), Pro (10 projects), and Enterprise (unlimited)."

Part 2: Data Model

Execute the Data Model

bash
/orchestre:execute-task (MCP) "Create data model for projects and tasks with team ownership"

This creates:

prisma
model Project {
  id             String   @id @default(cuid())
  name           String
  description    String?
  organizationId String
  organization   Organization @relation(...)
  tasks          Task[]
  createdAt      DateTime @default(now())
  updatedAt      DateTime @updatedAt
}

model Task {
  id          String   @id @default(cuid())
  title       String
  description String?
  status      TaskStatus @default(TODO)
  projectId   String
  project     Project  @relation(...)
  assigneeId  String?
  assignee    User?    @relation(...)
  dueDate     DateTime?
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt
}

enum TaskStatus {
  TODO
  IN_PROGRESS
  DONE
}

Run Migrations

bash
npm run db:migrate

Part 3: Subscription Setup

Configure Stripe Plans

bash
/setup-stripe

Add Subscription Plans

bash
/add-subscription-plan "Free plan: 1 project, 2 team members"
/add-subscription-plan "Pro plan: 10 projects, 10 team members, $29/month"
/add-subscription-plan "Enterprise plan: Unlimited projects and members, $99/month"

Implement Feature Gates

bash
/orchestre:execute-task (MCP) "Add subscription-based feature gates for project limits"

Part 4: Core Features

Project Management

bash
/add-feature "Project CRUD with team access control"

This creates:

  • app/(app)/projects/page.tsx - Project list
  • app/(app)/projects/[id]/page.tsx - Project details
  • app/api/projects/route.ts - API endpoints
  • Components for project UI

Task Management

bash
/add-feature "Task management within projects with status tracking"

Team Features

bash
/add-team-feature "Team member invitation system"
/add-team-feature "Role-based permissions (owner, admin, member)"

Part 5: User Interface

Dashboard

bash
/orchestre:execute-task (MCP) "Create dashboard showing user's projects and recent tasks"

The dashboard will include:

  • Project overview cards
  • Recent tasks list
  • Team activity feed
  • Quick actions

Project View

bash
/orchestre:execute-task (MCP) "Create Kanban board view for tasks"

This implements:

  • Drag-and-drop functionality
  • Status columns
  • Task cards with assignees
  • Real-time updates

Part 6: Advanced Features

Real-time Collaboration

bash
/orchestre:orchestrate (MCP) "Add real-time updates when team members modify tasks"

Search and Filtering

bash
/implement-search "Global search across projects and tasks"

Email Notifications

bash
/implement-email-template "Task assignment notification"
/implement-email-template "Project invitation"

Part 7: Testing the SaaS

Create Test Scenarios

bash
/orchestre:execute-task (MCP) "Create test scenarios for subscription upgrades and downgrades"

Manual Testing Flow

  1. Sign Up Flow

    • Register new account
    • Verify email (if configured)
    • Complete onboarding
  2. Team Creation

    • Create organization
    • Invite team members
    • Set permissions
  3. Subscription Flow

    • View pricing page
    • Select plan
    • Complete Stripe checkout
    • Verify features enabled
  4. Feature Usage

    • Create projects (check limits)
    • Add tasks
    • Invite members (check limits)
    • Test real-time updates

Part 8: Production Deployment

Pre-deployment Checklist

bash
/validate-implementation "production readiness"

Security Audit

bash
/orchestre:security-audit (MCP) --production

Performance Check

bash
/performance-check --web-vitals

Deploy to Production

bash
/deploy-production

Complete Code Example

Here's a key component - the Project List with subscription limits:

typescript
// app/(app)/projects/page.tsx
export default async function ProjectsPage() {
  const { organization, subscription } = await getOrgAndSubscription()
  const projects = await getProjects(organization.id)
  const canCreateMore = await canCreateProject(organization.id, subscription)

  return (
    <div>
      <PageHeader
        title="Projects"
        action={
          canCreateMore ? (
            <CreateProjectButton />
          ) : (
            <UpgradePrompt plan={subscription.plan} />
          )
        }
      />
      
      <ProjectGrid projects={projects} />
      
      {!canCreateMore && (
        <LimitReachedBanner
          current={projects.length}
          limit={subscription.projectLimit}
        />
      )}
    </div>
  )
}

Advanced Patterns

1. Subscription Middleware

typescript
// middleware/subscription-check.ts
export async function requiresPlan(minPlan: PlanLevel) {
  return async (req: Request) => {
    const subscription = await getUserSubscription(req)
    if (subscription.level < minPlan) {
      return redirectToUpgrade()
    }
  }
}

2. Team Context Hook

typescript
// hooks/use-team-context.ts
export function useTeamContext() {
  const { organization, membership } = useAuth()
  
  const can = (permission: Permission) => {
    return hasPermission(membership.role, permission)
  }
  
  return { organization, membership, can }
}

3. Real-time Updates

typescript
// components/task-board.tsx
export function TaskBoard({ projectId }) {
  const tasks = useLiveQuery(
    db.tasks.where({ projectId }).orderBy('position')
  )
  
  const moveTask = async (taskId, newStatus) => {
    await updateTask(taskId, { status: newStatus })
    // Optimistic update handled by useLiveQuery
  }
  
  return <KanbanBoard tasks={tasks} onMove={moveTask} />
}

Common Challenges

1. Subscription State

Problem: Subscription state not updating after payment Solution: Implement Stripe webhooks properly

bash
/orchestre:execute-task (MCP) "Debug Stripe webhook handling for subscription updates"

2. Multi-tenant Data Isolation

Problem: Users seeing data from other organizations Solution: Always filter by organizationId

bash
/orchestre:security-audit (MCP) --focus "data isolation"

3. Performance with Many Teams

Problem: Slow queries with many organizations Solution: Add proper indexes

bash
/performance-check --database

Best Practices

1. Always Use Organization Context

typescript
// ❌ Bad
const projects = await db.projects.findMany()

// ✅ Good
const projects = await db.projects.findMany({
  where: { organizationId: ctx.organizationId }
})

2. Feature Flag Everything

typescript
// Check features before showing UI
if (subscription.features.includes('advanced-analytics')) {
  return <AnalyticsDashboard />
}

3. Handle Subscription Limits Gracefully

typescript
// Show upgrade prompts contextually
if (isApproachingLimit) {
  return <SoftUpgradePrompt />
}

Practice Exercises

1. Add Comments Feature

bash
/orchestre:orchestrate (MCP) "Add commenting system to tasks with mentions"

2. Implement Audit Logs

bash
/orchestre:add-enterprise-feature (MCP) "Audit logging for all actions"

3. Add API Access

bash
/add-feature "Public API with key authentication"

4. Mobile App Integration

bash
/orchestre:orchestrate (MCP) "Plan mobile app that connects to this SaaS backend"

Production Checklist

  • [ ] Environment variables configured
  • [ ] Database migrations run
  • [ ] Stripe products created
  • [ ] Email service configured
  • [ ] Error tracking setup
  • [ ] Analytics configured
  • [ ] Security headers added
  • [ ] Performance optimized
  • [ ] Backup strategy defined
  • [ ] Monitoring established

What You've Learned

✅ Built a complete multi-tenant SaaS ✅ Implemented subscription billing ✅ Added team collaboration ✅ Created production-ready features ✅ Deployed to production

Next Steps

You've built a real SaaS that you could launch! Consider:

Continue learning: Parallel Workflows →

Enhance your SaaS:

  • Add more features with /orchestre:orchestrate (MCP)
  • Implement mobile app
  • Add enterprise features
  • Optimize performance

Launch it:

  • Set up marketing site
  • Configure production Stripe
  • Add terms of service
  • Start getting customers!

Remember: You've just built something people pay for. That's powerful!

Built with ❤️ for the AI Coding community, by Praney Behl