When I first started building SaaS products, I spent way too much time configuring boilerplate instead of writing actual feature code. I’d get halfway through setting up Stripe webhooks, only to realize my authentication flow was broken, or my API routes were timing out for users outside my home region.
It felt like I was building the same plumbing over and over. Every new project meant re-learning how to securely handle subscription state, re-configuring OAuth providers, and re-wiring up deployment pipelines. The creative momentum would just evaporate. So I decided to build something.
The Problem with SaaS Plumbing
The cost of ignoring a solid architectural foundation in the early stages of a SaaS is high. When you hack together a quick Stripe integration without proper webhook handling, you inevitably end up with desynchronized billing states. When you ignore edge computing early on, you build a centralized app that is inherently slow for global users, and refactoring it later requires tearing out the core API routes.
Boilerplate isn't just about saving a few hours of setup; it's about enforcing best practices so you don't accidentally introduce security flaws or performance bottlenecks. If your starter kit doesn't handle edge cases, your production app won't either.
Technical Approach: Building for the Edge
Over the past few months, I’ve distilled my learnings into a practical approach for building a modern SaaS stack. I focused on Next.js 14+ App Router, Stripe, and Cloudflare Workers. Here are the core technical decisions I made.
1. Enforce Type Safety in Billing Workflows
When dealing with money, you can't rely on loose JavaScript objects. I made it a rule to use TypeScript strictly for all Stripe interactions. This means defining exact types for price IDs and ensuring webhook payloads are properly parsed and verified.
Here is a practical example of how I structure a Next.js App Router API route for Stripe checkout. Notice how we pass the userId as a client_reference_id to tie the session back to our internal database without exposing it in the URL.
// Next.js API route for Stripe checkout (App Router)
import { NextResponse } from 'next/server';
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function POST(req: Request) {
const { priceId, userId } = await req.json();
const session = await stripe.checkout.sessions.create({
mode: 'subscription',
payment_method_types: ['card'],
line_items: [{ price: priceId, quantity: 1 }],
success_url: `${process.env.APP_URL}/dashboard?success=true`,
cancel_url: `${process.env.APP_URL}/pricing?cancelled=true`,
client_reference_id: userId,
});
return NextResponse.json({ url: session.url });
}
2. Push Heavy Lifting to the Edge
Traditional serverless functions are great, but if your SaaS has tools that fetch external data (like an SEO analyzer or link checker), running them from a single region adds unnecessary latency. I decided to use Cloudflare Workers for edge-side API routes.
By running the analysis at the edge, the code executes physically closer to the user. Here is how I built an edge SEO analyzer. It fetches the target HTML and runs regex checks without needing a heavy DOM parser, keeping the Worker execution time and memory footprint tiny.
// Cloudflare Worker for edge SEO analysis
export default {
async fetch(request, env) {
const url = new URL(request.url);
if (url.pathname !== '/api/analyze') {
return new Response('Not found', { status: 404 });
}
const targetUrl = url.searchParams.get('url');
if (!targetUrl) {
return new Response(JSON.stringify({ error: 'Missing url' }), {
status: 400,
headers: { 'Content-Type': 'application/json' },
});
}
// Fetch target page at the edge (low latency worldwide)
const response = await fetch(targetUrl, {
cf: { cacheTtl: 300, cacheEverything: true },
});
const html = await response.text();
// Run SEO checks (regex-based, no DOM needed)
const title = html.match(/<title[^>]*>([^<]*)<\/title>/i)?.[1] || '';
const desc = html.match(/<meta[^>]*name=["']description["'][^>]*content=["']([^"']*)["']/i)?.[1] || '';
const h1Count = (html.match(/<h1[ >]/gi) || []).length;
return new Response(JSON.stringify({ title, desc, h1Count }), {
headers: { 'Content-Type': 'application/json' },
});
},
};
3. Standardize Authentication and Deployment
For authentication, I leaned on NextAuth.js, ensuring all OAuth providers are typed and session management is handled securely. I spent time ensuring the TypeScript types for the session object were extended correctly to include custom database user IDs.
Regarding deployment, the friction of setting up CI/CD for a new project is often underestimated. I configured one-click deployment scripts for both Vercel and Cloudflare Pages. This means environment variables, build commands, and output directories are pre-configured, removing the guesswork from going live.
Practical Results
Building this foundation changed how I approach new projects. Moving the SEO analysis tool to Cloudflare Workers significantly reduced latency for global users, making the tool feel instantaneous regardless of where the user is located.
What surprised me the most was how much time the strict TypeScript billing setup saved during debugging. Because the types enforced the correct payload structures, I caught several edge cases in webhook handling before they ever reached production. When Stripe sends a complex webhook, the compiler caught mismatched properties that would have otherwise caused silent failures.
I also noticed that keeping the Cloudflare Worker logic simple (using regex instead of a full HTML parser) was crucial. It kept the execution time well under the free tier limits and ensured the tool remained fast and cost-effective to run.
If I were to change anything, I might abstract the database layer a bit more in the future. However, keeping it close to the surface in a boilerplate is actually better, as it allows developers to easily swap out their preferred ORM or database provider without fighting an overly abstracted layer.
Checking Your Own SaaS SEO
If you are currently working on a SaaS and want to evaluate your landing page before launch, I put together a free tool to help with that. You can use the SEO checker to analyze your meta tags, headings, and basic structure right in the browser.
You can try it out here: https://saaslaunch.dev/free-tools/seo-checker
Full disclosure: I'm the developer of SaaSLaunch. I built this because I needed it myself.
Top comments (0)