DEV Community

seller-mind
seller-mind

Posted on

How I Cured My SaaS Setup Fatigue and Finally Shipped My App

When I first decided to build a SaaS, I had a great idea for a tool. But before I could write a single line of actual business logic, I hit the "setup wall." I spent three weeks just wiring up Next.js App Router, configuring NextAuth, and trying to get Stripe webhooks to play nicely with my local environment. By the time I had a basic checkout flow working, my motivation was completely drained. I realized I was spending 80% of my time on infrastructure and 20% on my actual product. So I decided to build something.

The Problem with the Setup Tax

The problem isn't that these tools are hard to use. Next.js, Stripe, and Cloudflare are all fantastic. The problem is the "setup tax" you pay every time you start a new project. You have to make the same architectural decisions, write the same boilerplate, and debug the same edge cases.

If you ignore this setup tax, you risk one of two things. First, you burn out before launch. Second, you rush the foundation, leading to a fragile codebase that becomes a nightmare to maintain once you get your first few users. You end up rewriting your billing logic or auth flow when you should be focusing on feature development.

Technical Approach: Solid Patterns for SaaS

To solve this for myself, I started documenting and extracting the patterns that actually worked. Here are the concrete technical decisions I made to build a reliable SaaS foundation without the fatigue.

1. Keep Billing Logic Strictly Typed

When integrating Stripe, it is tempting to just pass raw strings for price IDs. But if you have multiple tiers, passing the wrong ID to the checkout session is a silent failure until the user complains. I enforce strict typing for my Stripe products. I define my price IDs in a central config file and use TypeScript to ensure the checkout API route only accepts valid IDs.

Here is the App Router API route I use for Stripe checkout. Notice how the priceId is passed directly from the client, but in a production setup, I validate it against an allowed list of price IDs before creating the session:

// 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 });
}
Enter fullscreen mode Exit fullscreen mode

2. Move Read-Heavy Tools to the Edge

For tools like SEO analyzers or link checkers, you are mostly reading external HTML and parsing it. Doing this in a standard serverless function can introduce latency for global users. I moved these read-heavy API routes to Cloudflare Workers.

Here is how I structured the edge worker for an SEO analyzer. Notice how I use Cloudflare's caching features and avoid heavy DOM parsing to keep the worker lightweight and fast:

// 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' },
    });
  },
};
Enter fullscreen mode Exit fullscreen mode

By using regex instead of a full DOM parser, the worker executes in milliseconds. The cacheTtl setting also ensures that if multiple users check the same URL, we don't hammer the target server.

3. Centralize Authentication Early

Don't bolt auth on at the end. I use NextAuth.js from day one, configured with OAuth providers. I set up middleware in the Next.js App Router to protect routes globally. This means I never have to remember to add an if (!session) check at the top of every new page. It is handled in one place.

Practical Results

Extracting these patterns into a reusable starter kit changed my workflow entirely. What worked really well was the edge deployment for the SEO tools. The global latency drop was noticeable immediately, and the Cloudflare Workers environment handled the regex parsing without breaking a sweat.

What surprised me was how much mental energy I saved by having the Stripe webhook handlers already written and tested. When I needed to add a new pricing tier, it turned a multi-hour debugging session into a quick config change.

If I could change one thing, I would simplify my initial database schema. I originally tried to account for every possible feature, which led to complex relational queries. I have since stripped it back to just users, subscriptions, and core resources. Keep the schema as flat as possible until you actually need the complexity.

Try the Edge SEO Tool

If you are tired of the setup tax and want to skip the boilerplate phase, I packaged all these patterns into a production-ready starter kit called SaaSLaunch. It includes the Next.js 14 App Router setup, the Stripe billing flows, and the Cloudflare Workers edge tools we talked about.

If you just want to test out the edge SEO analyzer first, I built a free version of it. You can try the SEO Checker right here: https://saaslaunch.dev/free-tools/seo-checker

It runs on the same Cloudflare Workers architecture and gives you a quick look at how the edge parsing performs.


Full disclosure: I'm the developer of SaaSLaunch. I built this because I needed it myself.

Top comments (0)