NestJS SaaS
starter
Auth, billing, teams, admin, and 14 pages — backend and frontend, wired together. Ship product, not plumbing.
Built by Shantanu Sinha
$199 one-time · Private repo access after purchase · Docs + quickstart
// billing.service.ts
async handleWebhookEvent(payload: string, headers: WebhookHeaders) {
// Verify webhook signature
const event = this.dodo.webhooks.unwrap(payload, { headers });
// Idempotency — skip duplicate events
const seen = await this.prisma.webhookEvent.findUnique({
where: { eventId: headers["webhook-id"] },
});
if (seen) return { received: true };
await this.processEvent(event);
await this.prisma.webhookEvent.create({ data: { eventId: headers["webhook-id"] } });
} Numbers below are from the repo as shipped — useful for sanity-checking scope, not hype.
See what ships
A real feature traced through every layer — from HTTP request to email notification. This is one flow out of dozens.
Verify signature
Payment provider signs every webhook. SDK verifies before any processing.
// billing.service.ts — verify the webhook is authentic
try {
event = this.dodo.webhooks.unwrap(payload, { headers });
} catch (err) {
const message = err instanceof Error ? err.message : "Unknown error";
this.logger.error(`Webhook signature verification failed: ${message}`);
throw new BadRequestException("Invalid webhook signature");
} Idempotency guard
Duplicate webhook? Skip silently — no double charges, no errors.
// Duplicate webhook? Skip silently — no double charges
const existing = await this.prisma.webhookEvent.findUnique({
where: { eventId: webhookId },
});
if (existing) {
this.logger.log(`Webhook event ${webhookId} already processed, skipping`);
return { received: true };
} Create purchase record
Persist the payment with a unique payment ID.
await this.prisma.purchase.create({
data: {
userId,
paymentId,
amount,
currency,
status: "completed",
},
}); Notify buyer & admin
Fire-and-forget emails — the payment is already safe in the database.
// Non-blocking — payment is already recorded
this.emailService
.sendPurchaseConfirmation(
user.email, user.name ?? user.email, amountFormatted
)
.catch((err: unknown) =>
this.logger.error("Failed to send purchase confirmation", err)
); Record webhook event
Mark as processed so duplicates hit the guard in step 2.
// Mark processed — future duplicates will hit the idempotency guard
await this.prisma.webhookEvent.create({
data: {
eventId: webhookId,
type: eventType,
},
});
return { received: true }; Test the edge case
The idempotency guard is tested: duplicate webhooks must not create purchases.
it("should skip if webhook already processed (idempotency)", async () => {
prisma.webhookEvent.findUnique.mockResolvedValue({
id: "wh-1", eventId: "wh_test_123",
type: "payment.succeeded", processedAt: new Date(),
});
const result = await service.handleWebhookEvent("payload", headers);
expect(result).toEqual({ received: true });
expect(prisma.purchase.create).not.toHaveBeenCalled();
}); This website runs on the same codebase. You're already using it in production.
What's in the NestJS SaaS Starter
10 backend modules, 14 frontend pages, and production-minded defaults — so you spend time on product, not re-solving auth and billing.
Auth you can ship
JWT + refresh rotation (bcrypt-hashed refresh tokens), Google OAuth, and magic link — wired with Passport.js patterns you can extend.
Billing end-to-end
Checkout, webhooks with signature verification, idempotency via a WebhookEvent table, and purchase tracking — ready to map to your plans.
Dashboard & app shell
React 19 dashboard with onboarding checklist, stat cards, quick actions, sidebar navigation, and team switcher. Dark theme, shadcn/ui components throughout.
Auth, settings & admin UI
Login, register, and OAuth pages. Settings with profile, billing, team, and danger zone tabs. Admin panel with user management. 14 pages ready to white-label.
Teams & invites
Organizations with owner/admin/member roles, email invitations with expiry, and team switching — the usual SaaS multi-tenant baseline.
Admin surface
User management and subscription visibility for operators. Built for a real SaaS, not a toy admin page.
Transactional email
HTML email templates and a queue-backed processor (Resend when configured). Welcome, billing, and team flows have a place to start.
Uploads & CDK-ready storage
Presigned S3 uploads and an AWS CDK stack path for storage + CDN when you want full cloud deployment.
Queues when Redis is on
BullMQ + Redis for async work (e.g. email). Degrades cleanly when Redis is not configured locally.
OpenAPI on every route
Swagger decorators across controllers so you always have a live contract while you build features.
Strict TypeScript monorepo
Shared types in packages, class-validator on DTOs, Zod for env — fewer foot-guns between API and UI.
Docs & deploy guidance
Quickstart, module guides, and a practical deploy path (Railway + optional CDK). Meant to answer “what do I do next?” after clone.
What's in the repo
10 backend modules, 7 frontend page groups, shared types, Prisma schema, and deployment config — all wired together.
Simple pricing
One-time purchase. Everything included. No subscription lock-in for the starter itself.
Complete starter
Pay once, own the full codebase. Secure checkout — instant access after payment.
- Full NestJS 11 + React 19 monorepo source
- Backend: auth, billing, teams, admin, email, storage, jobs
- Frontend: dashboard, settings, auth pages, admin panel, billing & team UI
- Prisma + PostgreSQL, Swagger, Jest + API smoke tests
- Docs-oriented quickstart and module guides
- 30-min 1:1 onboarding call with the developer
- Direct bug support via email
- Private repo access after purchase
All sales are final after source code access is granted. By purchasing, you agree to our Terms and Refund Policy
We offer regional pricing. Your price may adjust based on location.
Honest scope — claims match what ships in the repo.
Frequently asked questions
Purchase, delivery, and what's in the box.