Skip to main content

Build & Ship a Web App in 48 Hours Using Cursor AI + Claude: A Non-Programmer's Guide

Build and ship a production-ready SaaS in 48 hours with Next.js 16, Supabase, and Cursor IDE using modern Cursor Project Rules (.cursor/rules/*.mdc).

The "Vibe-Coding" Shift: From Syntax Typing to Architecture Orchestration

The barrier to building software has experienced the most radical democratization in computing history.

In traditional software development, building and deploying a production-grade software-as-a-service (SaaS) web application required months of manual syntax typing: writing boilerplate CRUD endpoints, debugging database schema migrations, and wrestling with authentication state cookies.

Today, pairing Cursor IDE (an AI-native fork of VS Code) with frontier foundation models like Anthropic Claude 3.5 Sonnet enables a single developer or non-technical founder to ship production-ready web applications in 48 hours.

However, moving from a toy prototype to an enterprise-ready commercial SaaS requires discipline. When developers rely on undisciplined conversational prompting, AI assistants generate conflicting code patterns, duplicate state variables, and catastrophic security vulnerabilities.

This production guide outlines the complete 48-hour development blueprint: establishing a bulletproof Next.js 16 and Supabase stack, configuring modern Cursor Project Rules (.cursor/rules/*.mdc), enforcing PostgreSQL Row Level Security (RLS), and implementing authenticated Server Actions.


The Modern Production SaaS Stack

To maximize developer velocity while guaranteeing enterprise security and scalability, this blueprint utilizes four tightly integrated technologies:

┌────────────────────────────────────────────────────────┐
│  FRONTEND & APPLICATION LAYER: Next.js 16 (App Router) │
│  React Server Components, Tailwind CSS, TypeScript     │
└──────────────────────────┬─────────────────────────────┘
                           │ Authenticated Server Actions
                           ▼
┌────────────────────────────────────────────────────────┐
│  DATABASE & AUTHENTICATION: Supabase (PostgreSQL)       │
│  PostgreSQL with Row Level Security (RLS) + Auth JWTs  │
└──────────────────────────┬─────────────────────────────┘
                           │ Webhook Events / Signature HMAC
                           ▼
┌────────────────────────────────────────────────────────┐
│  PAYMENT GATEWAY: Stripe / Razorpay                    │
│  Hosted Checkout, Customer Portal, Webhook Handlers    │
└────────────────────────────────────────────────────────┘
  1. Framework: Next.js 16 (App Router) — utilizing React Server Components (RSC) for instant server rendering, sub-20ms TTFB, and secure Server Actions for database mutations.
  2. Backend & Auth: Supabase — providing hosted PostgreSQL, built-in user authentication (JWTs), and database-level security policies.
  3. Styling: Tailwind CSS — rapid utility-first styling ensuring lightweight CSS bundles (<15 KiB).
  4. Payments: Stripe or Razorpay — hosted checkout pages with server-side HMAC-SHA256 signature verification.

Configuring Cursor: Modern Project Rules (.cursor/rules/*.mdc)

In earlier versions of Cursor, developers relied on a single, monolithic .cursorrules file in the project root. While functional, large rules files quickly consumed context tokens and often confused the model with rules irrelevant to the active file.

Modern Cursor IDE versions introduce Cursor Project Rules: modular, markdown-based rule files stored in .cursor/rules/ that use YAML frontmatter to selectively target specific file patterns (globs).

Step 1: Establish Your Project Rules File

Create .cursor/rules/nextjs-supabase.mdc in your repository root:

---
description: Production standards for Next.js 16 App Router and Supabase
globs: src/**/*.ts, src/**/*.tsx, src/**/*.sql
alwaysApply: true
---

# Architecture & Engineering Standards

### 1. Component Architecture
- Use React Server Components (RSC) by default.
- Only add `"use client"` when component requires React hooks (useState, useEffect) or browser event listeners.
- Keep Client Components at the leaves of the component tree to maximize server pre-rendering.

### 2. Database & Data Mutation Standards
- Never query the database directly from Client Components.
- Execute all database mutations via Next.js Server Actions ('use server') located in `src/actions/`.
- Validate all incoming Server Action payloads through Zod schemas before database insertion.
- Never use the Supabase `service_role` key in client code. Only use it in authenticated server-side webhook handlers.

### 3. Styling & Design Language
- Use Tailwind CSS exclusively. No arbitrary inline style tags.
- Follow the ClaimSkills design system: `#FAF0F2` background, `#FFFFFF` cards, `#A11236` primary accents, `#1D1D22` body text.

### 4. Error Handling
- Never throw unhandled exceptions to the UI. Return typed `{ ok: boolean, data?: T, error?: string }` responses.

48-Hour Implementation Blueprint

Phase 1: Database Architecture & Row Level Security (Hours 0–12)

A common beginner vulnerability is querying the database from client components without access controls. In Supabase, Row Level Security (RLS) enforces authorization directly inside the PostgreSQL database engine.

Execute this DDL schema migration in the Supabase SQL Editor:

-- 1. Create Profiles Table Linked to Supabase Auth
create table public.profiles (
  id uuid references auth.users on delete cascade primary key,
  full_name text not null,
  email text unique not null,
  subscription_status text default 'free' check (subscription_status in ('free', 'active', 'cancelled')),
  created_at timestamp with time zone default timezone('utc'::text, now()) not null
);

-- 2. Create User Content / Projects Table
create table public.projects (
  id uuid default gen_random_uuid() primary key,
  user_id uuid references public.profiles(id) on delete cascade not null,
  name text not null,
  content jsonb default '{}'::jsonb,
  is_published boolean default false,
  updated_at timestamp with time zone default timezone('utc'::text, now()) not null,
  created_at timestamp with time zone default timezone('utc'::text, now()) not null
);

-- 3. Enable Row Level Security (MANDATORY)
alter table public.profiles enable row level security;
alter table public.projects enable row level security;

-- 4. Establish RLS Policies (Users can only view & edit their own records)
create policy "Users can view own profile"
  on public.profiles for select
  using (auth.uid() = id);

create policy "Users can update own profile"
  on public.profiles for update
  using (auth.uid() = id);

create policy "Users can view own projects"
  on public.projects for select
  using (auth.uid() = user_id);

create policy "Users can insert own projects"
  on public.projects for insert
  with check (auth.uid() = user_id);

create policy "Users can update own projects"
  on public.projects for update
  using (auth.uid() = user_id);

create policy "Users can delete own projects"
  on public.projects for delete
  using (auth.uid() = user_id);

Phase 2: Type-Safe Server Actions with Zod Validation (Hours 12–24)

Never trust incoming user input. Create a secure Server Action (src/actions/projects.ts):

"use server";

import { z } from "zod";
import { createServerClient } from "@supabase/ssr";
import { cookies } from "next/headers";
import { revalidatePath } from "next/cache";

// 1. Define Strict Input Schema
const CreateProjectSchema = z.object({
  name: z.string().min(2, "Project name must be at least 2 characters").max(50),
});

export async function createProjectAction(rawInput: unknown) {
  try {
    // 2. Validate Input Payload
    const parsed = CreateProjectSchema.parse(rawInput);

    // 3. Authenticate User Session
    const cookieStore = await cookies();
    const supabase = createServerClient(
      process.env.NEXT_PUBLIC_SUPABASE_URL!,
      process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
      {
        cookies: {
          get(name: string) {
            return cookieStore.get(name)?.value;
          },
        },
      }
    );

    const { data: { user }, error: authError } = await supabase.auth.getUser();
    if (authError || !user) {
      return { ok: false, error: "Unauthorized access. Please sign in." };
    }

    // 4. Insert Record (RLS automatically enforces auth.uid() check)
    const { data, error } = await supabase
      .from("projects")
      .insert({
        name: parsed.name,
        user_id: user.id,
      })
      .select()
      .single();

    if (error) {
      return { ok: false, error: error.message };
    }

    // 5. Revalidate Cache
    revalidatePath("/dashboard/projects");
    return { ok: true, data };
  } catch (err) {
    if (err instanceof z.ZodError) {
      return { ok: false, error: err.errors[0].message };
    }
    return { ok: false, error: "An unexpected error occurred while creating project." };
  }
}

Phase 3: Payment Gateway & Webhook Signature Verification (Hours 24–36)

Payments must be processed via webhooks to prevent client-side payment tampering. Create a route handler (src/app/api/webhooks/route.ts):

import { NextRequest, NextResponse } from "next/server";
import crypto from "crypto";
import { createClient } from "@supabase/supabase-js";

// Service role client bypasses RLS for administrative background updates
const supabaseAdmin = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_ROLE_KEY!
);

export async function POST(req: NextRequest) {
  const secret = process.env.PAYMENT_WEBHOOK_SECRET;
  if (!secret) {
    return NextResponse.json({ error: "Webhook secret missing" }, { status: 500 });
  }

  const rawBody = await req.text();
  const signature = req.headers.get("x-signature");

  // Verify HMAC-SHA256 Signature
  const expectedSignature = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");

  if (signature !== expectedSignature) {
    console.error("Invalid webhook signature attempt rejected.");
    return NextResponse.json({ error: "Invalid signature" }, { status: 401 });
  }

  const payload = JSON.parse(rawBody);

  // Handle Successful Payment Event
  if (payload.event === "payment.captured") {
    const userId = payload.data.userId;
    await supabaseAdmin
      .from("profiles")
      .update({ subscription_status: "active" })
      .eq("id", userId);
  }

  return NextResponse.json({ received: true });
}

Phase 4: Production Deployment & Domain Setup (Hours 36–48)

  1. Push your repository to GitHub.
  2. Import the project into Vercel and attach environment variables:
    • NEXT_PUBLIC_SUPABASE_URL
    • NEXT_PUBLIC_SUPABASE_ANON_KEY
    • SUPABASE_SERVICE_ROLE_KEY
    • PAYMENT_WEBHOOK_SECRET
  3. Configure your custom apex domain (yourdomain.com) with Cloudflare or Vercel DNS.
  4. Verify HTTPS TLS 1.3 issuance and test a live $1.00 checkout transaction end-to-end.

4 Catastrophic Security Mistakes to Avoid

  1. Exposing the Service Role Key on the Client: Never prefix your SUPABASE_SERVICE_ROLE_KEY with NEXT_PUBLIC_. The service role key bypasses all Row Level Security policies. If exposed to the browser, any user can delete or dump your entire database.
  2. Forgetting to Enable RLS: Simply creating tables in Supabase does not automatically enable RLS unless explicitly declared (alter table ... enable row level security;). Always verify the padlock icon in the Supabase Table Editor.
  3. Skipping Webhook Signature Checks: If your webhook endpoint parses payloads without verifying HMAC cryptographic signatures, malicious actors can forge fake payment notifications and grant themselves free subscriptions.
  4. Context Cluttering in Cursor: Avoid adding the entire node_modules or dist directories into your Cursor indexing. Ensure your .cursorignore ignores package locks, build artifacts, and environment secrets.

Frequently Asked Questions (FAQ)

What is the difference between legacy .cursorrules and modern .cursor/rules/*.mdc?

Legacy .cursorrules was a single file that applied universally across the entire project, consuming valuable context tokens on every query. Modern Cursor Project Rules (.cursor/rules/*.mdc) use YAML frontmatter with globs: (e.g., globs: src/components/**/*.tsx) so Cursor only loads specific rules when editing relevant files.

Why use Next.js Server Actions instead of traditional API routes?

Server Actions eliminate the need to write separate REST API endpoints (fetch('/api/projects')) and manage manual state serialization. They execute securely on the server with automatic TypeScript typing and seamless cache revalidation (revalidatePath).

Can a non-programmer genuinely build a SaaS using this guide?

Yes, provided you adopt an architectural mindset: rather than trying to write every line of JavaScript yourself, your job is to define clear database schemas, configure strict Cursor Project Rules, and review the AI's output for security and logical consistency.


Official References & Developer Documentation

CS
Published by ClaimSkills Tech Team
Verified ClaimSkills Educational Resource
Fact-Checked & Tested
Practical AI Certification Curriculum

Master These Workflows with Video Guidance

Turn these strategies into hands-on execution. Access complete step-by-step video lessons, unlock downloadable prompt templates, and receive verifiable digital credentials.

Includes Verifiable Certificate

Continue Reading

View all articles