Skip to content
This repository was archived by the owner on Nov 12, 2025. It is now read-only.
/ saas-boilerplate Public archive

Sample SaaS web app for coding interviews

Notifications You must be signed in to change notification settings

theori-io/saas-boilerplate

Repository files navigation

AI SaaS Template

A modern monorepo template for building AI-powered SaaS applications with Next.js, TypeScript, and AWS CDK.

Project Structure

saas-template/
├── apps/
│   ├── web/                 # Next.js 14 application (App Router)
│   └── api/                 # Node.js API server with Hono
├── packages/
│   └── shared/              # Shared types and constants
├── infra/                   # AWS CDK v2 infrastructure definitions
├── package.json             # Root package.json with workspace configuration
├── tsconfig.base.json       # Base TypeScript configuration
├── .editorconfig            # Editor configuration
├── .gitignore              # Git ignore rules
└── .nvmrc                  # Node.js version specification (22)

Workspaces

  • apps/web: Next.js 14 application with App Router, TypeScript, and Tailwind CSS
  • apps/api: Node.js API server with Hono, JWT authentication, and in-memory data store
  • packages/shared: Shared TypeScript types, constants, and utilities
  • infra: AWS CDK v2 infrastructure as code definitions

Getting Started

Prerequisites

  • Node.js 22 (specified in .nvmrc)
  • pnpm package manager
  • Clerk account (for authentication)

Installation

# Install dependencies for all workspaces
pnpm install

Authentication Setup (Clerk)

  1. Create a Clerk account:

  2. Create a new application:

    • Click "Add application"
    • Choose "Next.js" as the framework
    • Copy the API keys
  3. Configure environment variables:

    For the web app (apps/web/.env.local):

    # Clerk configuration
    NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_your_actual_key_here
    CLERK_SECRET_KEY=sk_test_your_actual_secret_here
    
    # API configuration
    API_BASE_URL=http://localhost:4000

    For the API server (apps/api/.env.local):

    # Clerk JWKS configuration
    CLERK_JWKS_URL=https://your-clerk-domain.clerk.accounts.dev/.well-known/jwks.json
    CLERK_JWT_ISSUER=https://your-clerk-domain.clerk.accounts.dev
  4. Test the authentication:

    • Run pnpm run dev (starts both web app on :3000 and API server on :4000)
    • Visit http://localhost:3000
    • Click "Sign Up" to create an account
    • Click "Protected App" to test authentication
    • Test the Notes CRUD functionality (requires authentication)
    • Test the Billing functionality (requires authentication)

Development

# Start both web app and API server concurrently
pnpm run dev

# Build all workspaces
pnpm run build

# Run linting across all workspaces
pnpm run lint

# Run type checking across all workspaces
pnpm run typecheck

# Run tests across all workspaces
pnpm run test

# Run tests in watch mode
pnpm run test:watch

# Run tests with coverage
pnpm run test:coverage

Individual Workspace Commands

# Web application
cd apps/web
pnpm dev          # Start development server
pnpm build        # Build for production
pnpm start        # Start production server
pnpm lint         # Run ESLint
pnpm typecheck    # Run TypeScript type checking
pnpm test         # Run web app tests
pnpm test:watch   # Run tests in watch mode
pnpm test:coverage # Run tests with coverage

# API server
cd apps/api
pnpm dev          # Start API server on port 4000
pnpm build        # Build TypeScript to JavaScript
pnpm typecheck    # Run TypeScript type checking
pnpm test         # Run API tests
pnpm test:watch   # Run tests in watch mode
pnpm test:coverage # Run tests with coverage

# Shared package
cd packages/shared
pnpm build        # Build shared types and utilities
pnpm typecheck    # Run TypeScript type checking

# Infrastructure
cd infra
pnpm build        # Build CDK definitions
pnpm typecheck    # Run TypeScript type checking
pnpm synth        # Synthesize CDK templates
pnpm cdk:bootstrap # Bootstrap CDK (first time only)
pnpm cdk:deploy   # Deploy infrastructure
pnpm cdk:destroy  # Destroy infrastructure

Technology Stack

  • Frontend: Next.js 14, React 19, TypeScript, Tailwind CSS
  • Authentication: Clerk (Next.js integration)
  • Backend: Node.js 22, TypeScript, Hono HTTP server
  • Database: DynamoDB (production) / In-memory (development)
  • CDN: CloudFront distribution for global content delivery
  • Billing: Stripe (subscriptions, checkout, customer portal)
  • Infrastructure: AWS CDK v2, TypeScript
  • Package Management: pnpm workspaces
  • Code Quality: ESLint, Prettier, TypeScript strict mode

Development Workflow

  1. Make changes to the appropriate workspace
  2. Run pnpm run typecheck to ensure type safety
  3. Run pnpm run lint to check code quality
  4. Run pnpm run build to build all workspaces
  5. Test the web application with pnpm run dev

API Server Features

The API server (apps/api) includes:

  • Hono HTTP server running on port 4000
  • JWT authentication using Clerk JWKS verification
  • Protected endpoints for Notes and Billing operations
  • In-memory data store for development (notes persist during server session)
  • CORS enabled for web app integration
  • Health check endpoint (/health)

API Endpoints

Health & Auth:

  • GET /health - Health check (public)

Notes Management (protected):

  • GET /notes - Get all notes
  • POST /notes - Create note
  • GET /notes/:id - Get specific note
  • PATCH /notes/:id - Update note
  • DELETE /notes/:id - Delete note

Billing Management (protected):

  • POST /billing/checkout - Create Stripe checkout session
  • POST /billing/portal - Create Stripe customer portal session

Authentication Features

The web application includes:

  • Sign In/Sign Up pages (/sign-in, /sign-up) using Clerk components
  • Protected route (/app) that redirects unauthenticated users to sign-in
  • User session management with automatic redirects
  • JWT token integration with API server
  • Responsive design with Tailwind CSS

Infrastructure Deployment

The infrastructure is defined using AWS CDK v2 and includes two stacks:

CoreStack

  • DynamoDB Table: On-demand billing with PK/SK and three GSIs for org-scoped data access
  • S3 Bucket: Private bucket for file attachments with encryption and block public access

ApiStack

  • API Gateway: HTTP API with CORS for web domain
  • Lambda Functions: Node.js functions for notes, billing, and attachments
  • Permissions: Least-privilege access to DynamoDB and S3

Prerequisites

  • AWS CLI configured with appropriate credentials
  • AWS CDK v2 installed globally: npm install -g aws-cdk

Deployment Order

Important: Deploy stacks in this specific order:

  1. Deploy CoreStack first:
pnpm -C infra cdk:deploy SaasTemplateCore
  1. Deploy ApiStack:
pnpm -C infra cdk:deploy SaasTemplateApi
  1. Update web app environment:
    • Get the API URL from the ApiStack outputs
    • Update NEXT_PUBLIC_API_BASE_URL in apps/web/.env.local
    • Rebuild the web app: pnpm -C apps/web build

First-time Setup

# Bootstrap CDK (only needed once per AWS account/region)
pnpm -C infra cdk:bootstrap

# Deploy CoreStack
pnpm -C infra cdk:deploy SaasTemplateCore

# Deploy ApiStack
pnpm -C infra cdk:deploy SaasTemplateApi

Infrastructure Details

  • DynamoDB Table: saas-template-core

    • Primary Key: PK (string), SK (string)
    • GSI1: Subject → Notes (ORG#<orgId>#SUBJECT#<subjectId>NOTE#<noteId>#<createdAtISO>)
    • GSI2: User → Notes (ORG#<orgId>#USER#<userId>NOTE#<noteId>#<createdAtISO>)
    • GSI3: Role/Indexing (for future admin features)
    • Billing: On-demand (pay-per-request)
    • Point-in-time recovery: Disabled (free tier optimization)
    • Server-side encryption enabled
    • Removal policy: DESTROY (for template)
  • S3 Bucket: saas-template-attachments-<account>-<region>

    • Private access only
    • Server-side encryption
    • Versioning: Disabled (free tier optimization)
    • Removal policy: DESTROY (for template)
  • Lambda Functions:

    • Runtime: Node.js 22.x
    • CloudWatch log retention: 7 days (free tier optimization)
    • Pay-per-request pricing
    • Auto-scaling based on demand
  • API Gateway:

    • HTTP API (v2) for better performance and cost
    • CORS enabled for web app integration
    • Pay-per-request pricing
    • Auto-scaling based on demand

Free Tier Optimizations

The infrastructure is optimized for AWS free tier usage:

  • DynamoDB: On-demand billing with PITR disabled
  • S3: Versioning disabled to minimize storage costs
  • Lambda: 7-day log retention to stay within free tier limits
  • API Gateway: HTTP API v2 for lower costs than REST API
  • CloudWatch: Minimal log retention to avoid charges

Billing Integration

The template includes full Stripe billing integration with subscription management:

Features

  • Subscription Checkout: Create Stripe checkout sessions for plan upgrades
  • Customer Portal: Manage subscriptions, payment methods, and billing history
  • Plan Management: Free, Pro, and Business tiers with environment-configurable pricing
  • Development Mode: Works without Stripe configuration using mock responses

Stripe Setup

Step 1: Create Stripe Account and Get API Keys

  1. Create a Stripe account:

  2. Get your API keys:

    • In the Stripe Dashboard, go to DevelopersAPI keys
    • Copy your Publishable key (starts with pk_test_)
    • Copy your Secret key (starts with sk_test_)
    • Important: Keep your secret key secure and never commit it to version control

Step 2: Create Products and Prices

  1. Navigate to Products:

    • In the Stripe Dashboard, go to ProductsProducts
    • Click "Add product"
  2. Create Free Plan (Optional - for consistency):

    • Name: "Free Plan"
    • Description: "Perfect for getting started"
    • Pricing model: One-time
    • Price: $0.00
    • Click "Save product"
    • Copy the Price ID (starts with price_)
  3. Create Pro Plan:

    • Name: "Pro Plan"
    • Description: "For growing teams"
    • Pricing model: Recurring
    • Price: $29.00
    • Billing period: Monthly
    • Click "Save product"
    • Copy the Price ID (starts with price_)
  4. Create Business Plan:

    • Name: "Business Plan"
    • Description: "For large organizations"
    • Pricing model: Recurring
    • Price: $99.00
    • Billing period: Monthly
    • Click "Save product"
    • Copy the Price ID (starts with price_)

Step 3: Set Up Webhooks (Production)

  1. Create Webhook Endpoint:

    • In the Stripe Dashboard, go to DevelopersWebhooks
    • Click "Add endpoint"
    • Endpoint URL: https://your-api-domain.com/billing/webhook
    • Events to send: Select these events:
      • checkout.session.completed
      • customer.subscription.created
      • customer.subscription.updated
      • customer.subscription.deleted
    • Click "Add endpoint"
  2. Get Webhook Secret:

    • Click on your newly created webhook endpoint
    • In the Signing secret section, click "Reveal"
    • Copy the Signing secret (starts with whsec_)

Step 4: Configure Environment Variables

For the API server (apps/api/.env.local):

# Stripe configuration
STRIPE_SECRET_KEY=sk_test_your_stripe_secret_key_here
STRIPE_WEBHOOK_SECRET=whsec_your_webhook_secret_here
STRIPE_PRICE_FREE=price_free_plan_id
STRIPE_PRICE_PRO=price_pro_plan_id
STRIPE_PRICE_BUSINESS=price_business_plan_id

# Optional: Custom URLs (defaults to localhost)
STRIPE_SUCCESS_URL=http://localhost:3000/app/billing?success=true
STRIPE_CANCEL_URL=http://localhost:3000/app/billing?canceled=true
STRIPE_RETURN_URL=http://localhost:3000/app/billing

For the web app (apps/web/.env.local):

# Stripe Price IDs (public)
NEXT_PUBLIC_STRIPE_PRICE_FREE=price_free_plan_id
NEXT_PUBLIC_STRIPE_PRICE_PRO=price_pro_plan_id
NEXT_PUBLIC_STRIPE_PRICE_BUSINESS=price_business_plan_id

Step 5: Test Your Integration

  1. Start the development server:

    pnpm run dev
  2. Test the billing flow:

    • Visit http://localhost:3000/app/billing
    • You should see the pricing plans without the "Stripe not configured" warning
    • Click on a paid plan to test the checkout flow
    • Use Stripe's test card numbers:
      • Success: 4242 4242 4242 4242
      • Decline: 4000 0000 0000 0002
      • 3D Secure: 4000 0025 0000 3155
  3. Test webhook events (Development):

    • Install Stripe CLI: brew install stripe/stripe-cli/stripe (macOS) or see Stripe CLI docs
    • Login to Stripe: stripe login
    • Forward webhooks to local server: stripe listen --forward-to localhost:4000/billing/webhook
    • Copy the webhook signing secret from the CLI output
    • Update your STRIPE_WEBHOOK_SECRET in apps/api/.env.local
    • Test webhook events by triggering actions in your app
    • Monitor webhook events in the Stripe Dashboard

Step 6: Production Configuration

  1. Switch to Live Mode:

    • In the Stripe Dashboard, toggle to Live mode
    • Get your live API keys and webhook secrets
    • Update your environment variables with live keys
  2. Update Webhook URL:

    • Update your webhook endpoint URL to your production API URL
    • Ensure your production server can receive webhook events
  3. Test with Real Cards:

    • Use real payment methods in test mode first
    • Verify all webhook events are processed correctly
    • Test subscription lifecycle (create, update, cancel)

Troubleshooting

Common Issues:

  1. "Stripe not configured" warning:

    • Ensure all environment variables are set correctly
    • Check that Price IDs are valid and active in Stripe Dashboard
    • Restart your development server after adding environment variables
  2. Webhook signature verification failed:

    • Verify the webhook secret is correct
    • Ensure the webhook endpoint URL is accessible
    • Check that the webhook is receiving the correct events
  3. Checkout session creation fails:

    • Verify the Price ID exists and is active
    • Check that the success/cancel URLs are accessible
    • Ensure the Stripe secret key has the correct permissions
  4. Customer portal not working:

    • Verify the customer exists in Stripe
    • Check that the customer has an active subscription
    • Ensure the return URL is accessible

Useful Stripe Resources:

Quick Reference

Required Environment Variables:

Variable Location Description Example
STRIPE_SECRET_KEY apps/api/.env.local Stripe secret key sk_test_...
STRIPE_WEBHOOK_SECRET apps/api/.env.local Webhook signing secret whsec_...
STRIPE_PRICE_PRO apps/api/.env.local Pro plan price ID price_...
STRIPE_PRICE_BUSINESS apps/api/.env.local Business plan price ID price_...
NEXT_PUBLIC_STRIPE_PRICE_PRO apps/web/.env.local Pro plan price ID (public) price_...
NEXT_PUBLIC_STRIPE_PRICE_BUSINESS apps/web/.env.local Business plan price ID (public) price_...

Test Card Numbers:

  • Success: 4242 4242 4242 4242
  • Decline: 4000 0000 0000 0002
  • 3D Secure: 4000 0025 0000 3155
  • Requires Authentication: 4000 0025 0000 3155

Webhook Events Handled:

  • checkout.session.completed - Store customer information
  • customer.subscription.created - Update plan entitlement
  • customer.subscription.updated - Update plan entitlement
  • customer.subscription.deleted - Update plan entitlement

Billing API Endpoints

  • POST /billing/checkout - Create checkout session for plan upgrade
  • POST /billing/portal - Create customer portal session for subscription management
  • GET /billing/entitlement - Get current billing entitlement and plan status
  • POST /billing/webhook - Stripe webhook handler for subscription events

Development vs Production

Local Development (pnpm run dev):

  • Uses HTTP server on port 4000
  • Direct database access (DynamoDB or in-memory)
  • Real-time development with hot reload
  • No AWS Lambda cold starts

Production Deployment (Lambda + API Gateway):

  • Serverless Lambda functions
  • API Gateway for HTTP routing
  • DynamoDB and S3 integration
  • Auto-scaling and pay-per-request

Billing Configuration:

Development Mode (no Stripe config):

  • Shows "Stripe not configured" message
  • Plan buttons are disabled with tooltips
  • Mock responses for testing billing flow
  • pnpm run dev works out of the box

Production Mode (with Stripe config):

  • Real Stripe checkout and portal sessions
  • Customer management by organization ID
  • Full subscription lifecycle management
  • Webhook signature verification for secure event processing
  • Real-time entitlement tracking and plan status updates

Webhook Integration

The system includes a secure webhook handler that processes Stripe events:

Supported Events:

  • checkout.session.completed - Stores customer information
  • customer.subscription.created/updated/deleted - Updates plan entitlements

Security:

  • Webhook signature verification using STRIPE_WEBHOOK_SECRET
  • Event validation and error handling
  • Graceful fallback for missing Stripe configuration

Entitlement System:

  • Real-time plan status tracking
  • Current plan display with billing dates
  • Upgrade/downgrade CTAs based on current entitlement
  • Mock mode support for development without Stripe

Testing Billing

  1. Without Stripe (default):

    pnpm run dev
    # Visit http://localhost:3000/app/billing
    # See mock billing interface
  2. With Stripe (configured):

    # Set environment variables in both apps
    pnpm run dev
    # Visit http://localhost:3000/app/billing
    # Test real Stripe checkout flow

Free Tier Optimization

AWS Free Tier Checklist

To stay within AWS free tier limits during development:

Development Mode:

  • Keep DAL=inmemory in apps/api/.env.local to avoid AWS calls during development
  • Use small test data sets (limit to 10-20 notes per test)
  • Run pnpm run dev for local development (no AWS resources used)

Infrastructure Optimizations:

  • DynamoDB: On-demand billing with PITR disabled
  • S3: Versioning disabled, minimal storage usage
  • Lambda: 7-day CloudWatch log retention
  • API Gateway: Pay-per-request pricing

Cleanup Commands:

# Clean up S3 bucket before destroying infrastructure
aws s3 rm s3://your-bucket-name --recursive

# Destroy stacks in order (ApiStack first, then CoreStack)
pnpm -C infra cdk:destroy SaasTemplateApi
pnpm -C infra cdk:destroy SaasTemplateCore

Environment Matrices

Component Development Production
Database In-memory (DAL=inmemory) DynamoDB (DAL=dynamo)
Storage Local filesystem S3 bucket
Authentication Clerk test keys Clerk live keys
Billing Mock/Stripe test Stripe live
Logs Console output CloudWatch (7-day retention)
API Local server (port 4000) Lambda + API Gateway
Frontend Next.js dev server S3 + CloudFront CDN

Development vs Production Commands

Local Development (Free tier friendly):

# Single command to start everything
pnpm run dev

# This starts:
# - Next.js app on http://localhost:3000
# - API server on http://localhost:4000
# - Uses in-memory database (no AWS calls)
# - Uses mock billing (no Stripe calls)

Production Deployment:

# Deploy infrastructure
pnpm -C infra cdk:deploy SaasTemplateCore
pnpm -C infra cdk:deploy SaasTemplateApi

# Build and deploy web app
pnpm -C apps/web build
# Deploy to your hosting provider (Vercel, Netlify, etc.)

Testing

The project includes comprehensive testing setup with Jest and React Testing Library.

Test Structure

apps/
├── api/
│   ├── src/
│   │   ├── __tests__/
│   │   │   ├── setup.ts           # Test setup and mocks
│   │   │   ├── lambda.test.ts     # Lambda function tests
│   │   │   ├── dal.test.ts        # Data access layer tests
│   │   │   └── integration.test.ts # Integration tests
│   │   └── ...
│   └── jest.config.js
└── web/
    ├── src/
    │   ├── __tests__/
    │   │   ├── billing.test.tsx   # Billing page tests
    │   │   └── components.test.tsx # Component tests
    │   └── ...
    ├── jest.config.js
    └── jest.setup.js

Test Coverage

  • API Tests: Lambda functions, DAL operations, authentication, billing flows
  • Web Tests: React components, pages, user interactions, API integration
  • Integration Tests: End-to-end flows, error handling, data persistence

Running Tests

# Run all tests
pnpm run test

# Run tests in watch mode (development)
pnpm run test:watch

# Run tests with coverage report
pnpm run test:coverage

# Run tests for specific workspace
pnpm -C apps/api test
pnpm -C apps/web test

Test Features

  • Mocking: AWS SDK, Stripe, Clerk authentication
  • Coverage: Comprehensive coverage reporting
  • Type Safety: Full TypeScript support in tests
  • Isolation: Each test runs in isolation with clean state
  • CI Ready: Configured for continuous integration

Deployment

The project includes comprehensive deployment automation with GitHub Actions and deployment scripts.

GitHub Actions Workflows

Production Deployment (deploy-production.yml)

  • Trigger: Push to main branch or manual dispatch
  • Steps: Test → Deploy Infrastructure → Deploy Web App
  • Environment: Production AWS + Vercel

Development Deployment (deploy-development.yml)

  • Trigger: Push to develop or feature/* branches, PRs, or manual dispatch
  • Steps: Test → Deploy Infrastructure → Deploy Web App (Preview)
  • Environment: Development AWS + Vercel Preview

Infrastructure Management (infrastructure.yml)

  • Trigger: Manual dispatch only
  • Actions: Deploy, Destroy, Diff, Synthesize
  • Environments: Production or Development

Deployment Scripts

Quick Deploy

# Deploy to development
./scripts/deploy.sh --environment development

# Deploy to production
./scripts/deploy.sh --environment production

# Dry run (see what would be deployed)
./scripts/deploy.sh --dry-run

# Skip tests (faster deployment)
./scripts/deploy.sh --environment development --skip-tests

Environment Setup

# Set up development environment
./scripts/setup-env.sh development

# Set up production environment
./scripts/setup-env.sh production

Required GitHub Secrets

See .github/SECRETS.md for complete setup instructions.

Key Changes from Vercel to AWS CDN:

  • Web app now deploys to S3 + CloudFront instead of Vercel
  • No Vercel secrets required
  • Additional AWS S3 and CloudFront permissions needed
  • CloudFront provides global CDN with custom domain support

Essential Secrets:

  • AWS credentials (production + development)
  • Clerk authentication keys
  • Stripe price IDs (optional)
  • Vercel deployment tokens

Manual Deployment

1. Infrastructure Deployment

# Install dependencies
pnpm install

# Build infrastructure
pnpm -C infra build

# Deploy CoreStack first
pnpm -C infra cdk:deploy SaasTemplateCore

# Deploy ApiStack
pnpm -C infra cdk:deploy SaasTemplateApi

2. Web Application Deployment

# Build web app
NEXT_PUBLIC_API_BASE_URL=https://your-api-url.com pnpm -C apps/web build

# Deploy to Vercel
vercel --prod

Environment-Specific Configuration

Component Development Production
AWS Stack SaasTemplateDev* SaasTemplate*
Vercel Preview deployment Production deployment
Clerk Test keys Live keys
Stripe Test mode Live mode
Domain *.vercel.app Custom domain

Monitoring and Troubleshooting

Check Deployment Status

  1. Go to GitHub Actions tab
  2. View workflow runs and logs
  3. Check AWS CloudFormation stacks
  4. Verify Vercel deployments

Common Issues

  • AWS Permissions: Ensure IAM user has required policies
  • Environment Variables: Verify all secrets are set correctly
  • Stack Dependencies: Deploy CoreStack before ApiStack
  • Build Failures: Check logs for missing dependencies or configuration

Next Steps

  • Add user profile management
  • Implement role-based access control
  • Add webhook handlers for Stripe events
  • Implement usage tracking and plan limits
  • Add admin dashboard for subscription management

About

Sample SaaS web app for coding interviews

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published