DEV Community

seller-mind
seller-mind

Posted on

Serverless vs Edge: Architecting a Modern SaaS Starter

When I first started building SaaS products, I kept running into the same architectural wall. I would set up a standard serverless backend, and everything worked fine locally. But once I deployed globally, the latency for certain API routes became noticeable. Specifically, when I tried to build tools that needed to fetch and analyze external web pages, the round-trip time from a centralized server was just too slow for a good user experience. I spent weeks tweaking caching headers and moving functions closer to users, but the boilerplate to manage authentication, billing, and edge routing was overwhelming. So I decided to build something.

The core problem with traditional serverless setups for global SaaS tools is the latency tax. When your API routes are tied to a single region, a user in Europe querying a database in the US experiences a built-in delay. For standard CRUD operations, this is usually fine. But when you are building interactive tools—like an SEO analyzer that needs to fetch a target URL, parse the HTML, and return metadata—every millisecond counts.

Ignoring this architectural reality means building a clunky product. Users expect instant feedback. If your tool takes three seconds to return a simple meta-tag check because of network hops, they will assume the tool is broken and bounce. Furthermore, stitching together authentication, Stripe billing, and edge functions from scratch often leads to a fragmented codebase that is hard to maintain.

To solve this, I had to compare a few different approaches. The first option was sticking to a monolithic server. While easy to reason about, it completely defeats the purpose of modern, scalable SaaS development. The second option was standard serverless functions. These scale beautifully, but they can suffer from cold starts, and running them globally requires complex routing configurations.

The third option, and the one I ultimately chose for the edge-side tools, was running API routes on Cloudflare Workers. Edge computing moves your code to data centers all over the world. The trade-off is that edge environments have strict limitations on execution time and bundle size. You cannot just import massive Node.js libraries. However, the benefit is near-zero cold starts and incredibly low latency for global users.

Let’s look at how this changes the code for an SEO analysis tool. Instead of routing the request to a central server, we handle it directly at the edge.

// 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 a Cloudflare Worker, the fetch to the target URL happens from a location physically closer to the user. Notice that we aren't using a heavy DOM parser like JSDOM to extract the title and description. Because edge environments restrict heavy dependencies, we rely on regex-based parsing. For basic SEO checks, this is highly efficient and keeps the bundle size tiny.

Of course, the edge is only half the battle. You still need a robust frontend and billing system. For the main application, I used Next.js 14 with the App Router. It provides a great developer experience and integrates seamlessly with edge deployments. For billing, handling Stripe subscriptions requires reliable webhook processing.

// 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

This App Router approach keeps the Stripe logic cleanly separated and type-safe, which is crucial when handling money. Combining a Next.js frontend with edge-processed API routes gives you the best of both worlds: a rich, interactive UI and highly responsive backend utilities.

Moving to this architecture yielded some practical results. The most obvious improvement was the latency for the SEO analysis tool. What used to take a couple of seconds for international users now resolves almost instantly.

What surprised me the most was how well regex parsing held up at the edge. I initially worried that skipping a full DOM parser would lead to brittle code that broke on slightly malformed HTML. But by keeping the regex patterns simple and focused only on standard meta tags and headers, it has been surprisingly robust.

If I were to do it again, I would probably implement more granular caching strategies for the edge fetches. Right now, the cacheTtl is set to a flat 300 seconds, but allowing users to bypass the cache or setting different TTLs based on the target URL's headers would make the tool even more flexible.

If you want to see how this edge-optimized SEO analysis works in practice, I put together a free SEO checker to analyze landing pages and highlight technical issues. You can try it out without needing to sign up or install anything.


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

Top comments (0)