Claude Code से SaaS boilerplate बनाएं: Next.js auth, billing, tenant और tests
Claude Code से paid SaaS starter बनाएं: Next.js, auth, billing, tenant, audit logs, tests और launch checklist।
SaaS boilerplate का मतलब है paid web product के लिए reusable आधार: authentication, billing, tenants, roles, email, dashboard, admin screen, audit logs, environment variables, tests, documentation और deployment checklist। Claude Code इस आधार को तेजी से बना सकता है, लेकिन अगर इसे paid product या sellable template बनाना है, तो demo से ज्यादा मजबूत boundaries चाहिए।
सबसे खतरनाक shortcut है यह मान लेना कि “app चल रही है, इसलिए तैयार है”। Tenant का मतलब है एक ही application के अंदर अलग company, workspace या customer account। अगर tenant check कमजोर है, तो एक customer दूसरे customer का data देख सकता है। RBAC यानी role-based access control: owner, admin, billing, member और viewer को एक जैसे permissions नहीं मिलने चाहिए। Audit log का मतलब है किसने, कब, क्या किया इसकी record trail। इसके बिना support, incident response और enterprise sales मुश्किल हो जाते हैं।
यह guide दिखाती है कि Claude Code को कैसे prompt करें और Next.js App Router, TypeScript, Prisma, Stripe और Resend के साथ ऐसा SaaS starter कैसे बनाएं जो paid product या productized template बन सके। Implementation करते समय official docs को source of truth मानें: Claude Code docs, Next.js docs, Auth.js, Prisma schema docs, Stripe webhooks, Resend docs और OWASP Authentication Cheat Sheet।
ClaudeCodeLab पर आगे पढ़ने के लिए secure authentication, RBAC implementation, Zod validation और Claude Code API development देखें।
Paid product से उल्टा design करें
पहला सवाल यह नहीं है कि Claude Code कौन से components generate करेगा। पहला सवाल है कि जो व्यक्ति यह starter खरीदेगा, वह क्या launch कर पाएगा। बहुत सारे screens वाला लेकिन weak tenant boundary वाला template कम valuable है। छोटा template भी बेहतर है अगर उसमें tests, documentation और review rules साफ हों।
| Use case | जरूरी base | Monetization path |
|---|---|---|
| Solo micro-SaaS | OAuth login, personal plan, Stripe Checkout, usage dashboard | सरल monthly subscription |
| B2B team tool | Tenants, invites, roles, billing owner, audit logs | per-seat या per-workspace pricing |
| Member content या template portal | buyer role, download history, email notifications, admin | paid template packs और courses |
| Internal AI workflow tool | SSO, approvals, IP या domain policy, operation logs | implementation consulting |
Boilerplate को legal, tax, privacy या security review का replacement न बताएं। Stripe tax settings, refunds, terms of service, privacy policy, data retention, customer support और permission review अभी भी human review मांगते हैं। Claude Code implementation speed बढ़ाता है, business responsibility नहीं हटाता।
flowchart LR
A["Marketing site"] --> B["Auth"]
B --> C["Tenant and roles"]
C --> D["Dashboard"]
C --> E["Billing"]
C --> F["Admin"]
D --> G["Audit logs"]
E --> G
F --> G
G --> H["Tests and release checklist"]
Claude Code को CLAUDE.md contract दें
अगर आप सिर्फ “SaaS app बना दो” लिखते हैं, तो Claude Code UI, API routes, database models और business rules को एक बड़े diff में मिला सकता है। CLAUDE.md task को contract बनाता है। सरल भाषा में, harness वह काम करने की जमीन है जो agent को reviewable बदलाव करने में मदद करती है।
# CLAUDE.md
## Product goal
Build a paid SaaS starter that can be reused for real products.
Do not claim the starter removes legal, tax, privacy, or security review.
## Stack
- Next.js App Router with TypeScript
- Prisma and PostgreSQL
- Auth.js for OAuth/session integration
- Stripe Checkout and billing webhooks
- Resend for transactional email
- Vitest and Playwright for acceptance tests
## Required boundaries
- Every business record belongs to a tenantId.
- Never trust tenantId from the browser without checking membership.
- Roles are OWNER, ADMIN, BILLING, MEMBER, VIEWER.
- Billing routes require OWNER or BILLING.
- Admin routes require OWNER or ADMIN.
- State-changing routes write an audit log.
- Secrets must be read through src/lib/env.ts and never hardcoded.
- Include tests for forbidden tenant access and webhook idempotency.
## Review output
After edits, list changed files, commands run, risks, and manual checks.
ये rules common shortcuts रोकते हैं: browser से आया tenantId trust करना, secrets को code में रखना, member को billing route देना, या state change बिना audit log के करना।
Starter को responsibility के हिसाब से organize करें
छोटा demo एक route folder में रह सकता है। SaaS नहीं। Auth, billing, team management, email और audit जल्दी overlap करते हैं, इसलिए folder structure में boundaries दिखनी चाहिए।
src/
app/
(marketing)/
(auth)/
(dashboard)/dashboard/page.tsx
(admin)/admin/page.tsx
api/
billing/checkout/route.ts
billing/webhook/route.ts
tenants/invite/route.ts
components/
dashboard/
pricing/
ui/
lib/
auth.ts
env.ts
prisma.ts
tenant.ts
audit.ts
email.ts
stripe.ts
tests/
acceptance/saas.spec.ts
prisma/
schema.prisma
इस structure से prompt भी precise बनते हैं। आप कह सकते हैं, “सिर्फ billing webhook और related tests update करो”, पूरी app को छूने की जरूरत नहीं।
Prisma schema में tenant boundary रखें
नीचे schema practical minimum है। Tenant company, workspace या customer account है। Membership user को tenant और role से जोड़ता है। Subscription Stripe state रखता है। AuditLog important changes record करता है।
// prisma/schema.prisma
enum Role {
OWNER
ADMIN
BILLING
MEMBER
VIEWER
}
enum Plan {
FREE
STARTER
PRO
}
enum SubscriptionStatus {
TRIALING
ACTIVE
PAST_DUE
CANCELED
}
model User {
id String @id @default(cuid())
email String @unique
name String?
memberships Membership[]
auditLogs AuditLog[]
createdAt DateTime @default(now())
}
model Tenant {
id String @id @default(cuid())
name String
slug String @unique
plan Plan @default(FREE)
memberships Membership[]
subscription Subscription?
auditLogs AuditLog[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Membership {
id String @id @default(cuid())
userId String
tenantId String
role Role @default(MEMBER)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
@@unique([userId, tenantId])
@@index([tenantId, role])
}
model Subscription {
id String @id @default(cuid())
tenantId String @unique
stripeCustomerId String @unique
stripeSubscriptionId String? @unique
status SubscriptionStatus @default(TRIALING)
priceId String?
currentPeriodEnd DateTime?
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
updatedAt DateTime @updatedAt
}
model AuditLog {
id String @id @default(cuid())
tenantId String
actorId String?
action String
metadata Json?
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
actor User? @relation(fields: [actorId], references: [id])
createdAt DateTime @default(now())
@@index([tenantId, createdAt])
}
Real product में invoices, addresses, usage counters, API keys, data retention और support notes भी जोड़ने पड़ सकते हैं। पहले दिन सब कुछ model करना जरूरी नहीं है। जरूरी यह है कि tenant boundary शुरू से ही अनदेखी न हो।
Environment variables को validate करें
Secrets code, screenshot या commit में नहीं होने चाहिए। .env.example में सिर्फ names रखें; real values Vercel, Cloudflare, AWS, GitHub Actions या आपके platform में रखें। env.ts missing keys को जल्दी पकड़ता है।
// src/lib/env.ts
import { z } from "zod";
export const env = z
.object({
DATABASE_URL: z.string().url(),
AUTH_SECRET: z.string().min(32),
NEXT_PUBLIC_APP_URL: z.string().url(),
STRIPE_SECRET_KEY: z.string().startsWith("sk_"),
STRIPE_WEBHOOK_SECRET: z.string().startsWith("whsec_"),
STRIPE_PRICE_STARTER: z.string().min(1),
RESEND_API_KEY: z.string().startsWith("re_"),
EMAIL_FROM: z.string().email(),
})
.parse(process.env);
Tenant और role server पर check करें
Browser tenantId भेज सकता है, लेकिन server को उस पर भरोसा नहीं करना चाहिए। Business data पढ़ने या बदलने से पहले server पर Membership load करें।
// src/lib/tenant.ts
import { Role } from "@prisma/client";
import { redirect } from "next/navigation";
import { auth } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
const roleRank: Record<Role, number> = {
VIEWER: 1,
MEMBER: 2,
BILLING: 3,
ADMIN: 4,
OWNER: 5,
};
export async function requireTenant(tenantId: string, minimumRole: Role = "MEMBER") {
const session = await auth();
if (!session?.user?.id) redirect("/login");
const membership = await prisma.membership.findUnique({
where: {
userId_tenantId: {
userId: session.user.id,
tenantId,
},
},
include: { tenant: true },
});
if (!membership || roleRank[membership.role] < roleRank[minimumRole]) {
throw new Error("Forbidden tenant access");
}
return {
userId: session.user.id,
tenant: membership.tenant,
role: membership.role,
};
}
Common failure यह है कि tenantId को hidden input में रखकर update में direct use कर लिया जाए। Hidden input secret नहीं होता। हर write operation में server-side Membership check करें।
Stripe Webhook को repeatable event मानें
Webhook external notification है। यह एक से अधिक बार आ सकता है या late आ सकता है। Production-ready starter में signature verification, metadata.tenantId, upsert और audit log होना चाहिए।
// src/app/api/billing/webhook/route.ts
import Stripe from "stripe";
import { headers } from "next/headers";
import { NextResponse } from "next/server";
import { SubscriptionStatus } from "@prisma/client";
import { env } from "@/lib/env";
import { prisma } from "@/lib/prisma";
const stripe = new Stripe(env.STRIPE_SECRET_KEY);
function toStatus(status: Stripe.Subscription.Status): SubscriptionStatus {
if (status === "active") return "ACTIVE";
if (status === "past_due") return "PAST_DUE";
if (status === "canceled") return "CANCELED";
return "TRIALING";
}
export async function POST(request: Request) {
const body = await request.text();
const signature = (await headers()).get("stripe-signature");
if (!signature) {
return NextResponse.json({ error: "Missing Stripe signature" }, { status: 400 });
}
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(body, signature, env.STRIPE_WEBHOOK_SECRET);
} catch {
return NextResponse.json({ error: "Invalid Stripe signature" }, { status: 400 });
}
if (
event.type === "customer.subscription.created" ||
event.type === "customer.subscription.updated" ||
event.type === "customer.subscription.deleted"
) {
const subscription = event.data.object as Stripe.Subscription;
const tenantId = subscription.metadata.tenantId;
if (!tenantId || typeof subscription.customer !== "string") {
return NextResponse.json({ error: "Missing tenant metadata" }, { status: 400 });
}
await prisma.subscription.upsert({
where: { tenantId },
create: {
tenantId,
stripeCustomerId: subscription.customer,
stripeSubscriptionId: subscription.id,
status: toStatus(subscription.status),
priceId: subscription.items.data[0]?.price.id,
currentPeriodEnd: new Date(subscription.current_period_end * 1000),
},
update: {
stripeSubscriptionId: subscription.id,
status: toStatus(subscription.status),
priceId: subscription.items.data[0]?.price.id,
currentPeriodEnd: new Date(subscription.current_period_end * 1000),
},
});
await prisma.auditLog.create({
data: {
tenantId,
action: `stripe.${event.type}`,
metadata: { eventId: event.id, subscriptionId: subscription.id },
},
});
}
return NextResponse.json({ received: true });
}
Email और audit को shared helpers में रखें
Invites, failed payments, password reset और admin alerts email use करते हैं। Billing changes, role changes और tenant settings audit logs मांगते हैं। Shared helpers से product review आसान होता है।
// src/lib/email.ts
import { Resend } from "resend";
import { env } from "@/lib/env";
const resend = new Resend(env.RESEND_API_KEY);
export async function sendTenantInviteEmail(input: {
to: string;
tenantName: string;
inviteUrl: string;
}) {
return resend.emails.send({
from: env.EMAIL_FROM,
to: input.to,
subject: `${input.tenantName} invited you`,
html: `<p>You were invited to ${input.tenantName}.</p><p><a href="${input.inviteUrl}">Accept invite</a></p>`,
});
}
// src/lib/audit.ts
import { prisma } from "@/lib/prisma";
export async function writeAuditLog(input: {
tenantId: string;
actorId?: string;
action: string;
metadata?: Record<string, unknown>;
}) {
return prisma.auditLog.create({
data: {
tenantId: input.tenantId,
actorId: input.actorId,
action: input.action,
metadata: input.metadata,
},
});
}
UI polish से पहले acceptance tests तय करें
Acceptance test यह जांचता है कि feature user की नजर से acceptable है या नहीं। Paid SaaS starter में refusal paths अक्सर happy path जितने ही जरूरी होते हैं।
// tests/acceptance/saas.spec.ts
import { test, expect } from "@playwright/test";
test("member cannot open billing settings", async ({ page }) => {
await page.goto("/test-login?role=MEMBER");
await page.goto("/dashboard/acme/billing");
await expect(page.getByText("Forbidden")).toBeVisible();
});
test("billing user can open billing settings", async ({ page }) => {
await page.goto("/test-login?role=BILLING");
await page.goto("/dashboard/acme/billing");
await expect(page.getByRole("heading", { name: "Billing" })).toBeVisible();
});
test("tenant switch does not leak another tenant data", async ({ page }) => {
await page.goto("/test-login?tenant=acme");
await page.goto("/dashboard/other-team/settings");
await expect(page.getByText("Forbidden")).toBeVisible();
});
Common mistake है सिर्फ login, workspace creation, payment और dashboard दिखना test करना। यह भी test करें कि दूसरा tenant block होता है, member billing change नहीं कर सकता और repeated webhook subscription state खराब नहीं करता।
Launch checklist
Publish या sell करने से पहले कम से कम ये checks करें।
- Metadata, hero image, official links, internal links और CTA मौजूद हैं
.env.exampleमें names हैं, real secrets नहीं- हर business update server-side tenant membership check करता है
- OWNER, ADMIN, BILLING, MEMBER और VIEWER differences test हुए हैं
- Stripe Webhook signature verification और idempotency मौजूद है
- Email failures के लिए retry या manual resend path है
- Admin screen और audit logs production में काम करते हैं
- Terms, privacy policy, refund policy, tax settings और data retention human review से गुजरे हैं
- README में local setup, seed, tests और deployment steps हैं
Product की तरह package करें
अगर आप SaaS boilerplate बेचना चाहते हैं, तो code value का सिर्फ एक हिस्सा है। Buyers को setup docs, environment variable map, Stripe configuration, seed data, acceptance tests और support boundary भी चाहिए।
| Package | Contents | Best fit |
|---|---|---|
| Free checklist | CLAUDE.md sample, env map, release checklist | Idea validate करने वाले solo builders |
| Starter template | Next.js, Prisma, Auth.js, Stripe, Resend, tests | Weekend में MVP बनाने वाले |
| Pro template | Admin, audit logs, usage billing, invites, docs | Paid SaaS seriously launch करने वाले |
| Team rollout | Repo review, Claude Code training, review rules | Claude Code adopt करने वाली teams |
ClaudeCodeLab में free cheatsheet, Claude Code products और templates और training या implementation consulting उपलब्ध हैं। Free material से workflow validate करें, फिर real repository में fit करने के लिए templates या consulting चुनें।
Practical result
Masa ने एक छोटे validation repository में पहले बहुत broad prompt दिया: “dashboard बना दो”। Screen चल गई, लेकिन दूसरे tenant URL पर direct navigation block नहीं हुआ और Stripe Webhook में signature verification missing था। जब tenant boundaries, roles, audit logs और acceptance tests को CLAUDE.md में लिखा गया, Claude Code ने छोटे diffs बनाए और review checklist साफ हुई। Lesson सीधा है: SaaS boilerplate की value feature count में नहीं, boundaries, tests और operating docs में होती है।
मुफ़्त PDF: Claude Code cheatsheet
Email डालें और commands, review habits तथा safe workflow वाली एक-page PDF पाएँ.
हम आपका data सुरक्षित रखते हैं और spam नहीं भेजते.
लेखक के बारे में
Masa
Claude Code workflow और team adoption पर काम करने वाला engineer.
संबंधित लेख
Claude Code Obsidian to CLAUDE.md workflow: context बार-बार न समझाएं
Obsidian notes को CLAUDE.md operating notes में बदलकर Claude Code sessions को resume करना आसान बनाएं.
Claude Code Revenue CTA Routing: article से PDF, Gumroad और consultation तक
Reader intent के आधार पर free PDF, Gumroad products और consultation तक CTA route करने वाला workflow.
Claude Code टीम हैंडऑफ नियम: review proof, permissions, rollback और revenue path
Claude Code टीम काम के लिए evidence, permission rules, rollback, free PDF, Gumroad और consultation path वाला handoff.