When I first started building my SaaS, I made the classic mistake of putting absolutely everything into a single Next.js monolith. The core application, the Stripe billing, the user dashboard, and a few auxiliary tools like an SEO analyzer all lived in the same Vercel deployment. For a while, it was fine. But as soon as I added the SEO analysis tool, things started to crack. Fetching external HTML and parsing it inside standard serverless functions led to frustrating timeout errors and sluggish response times for users outside the US. I needed a way to handle these heavy, I/O-bound tasks globally without sacrificing the developer experience of my main app. So I decided to build something.
The Problem
When building SaaS products, we often default to a single deployment target because it is convenient. Next.js on traditional serverless platforms is highly effective for the core application. It handles server-side rendering, complex database queries, authentication via NextAuth.js, and Stripe webhook processing beautifully.
However, when you introduce auxiliary micro-tools—like a web scraper, an SEO checker, or an image optimizer—the trade-offs of a monolithic serverless approach become very apparent. Traditional serverless functions have strict execution time limits. They also suffer from cold start penalties. If a user in Europe triggers an SEO analysis tool hosted on US-East serverless functions, the latency spikes.
Ignoring this architectural mismatch means a poor user experience. More importantly, it means you risk hitting execution timeouts, which crashes the tool entirely and leaves your users frustrated. You end up paying for cold starts while delivering a subpar experience.
Technical Approach
To solve this, I had to objectively compare two approaches: keeping everything in traditional serverless, or splitting the workload by moving the I/O heavy tools to the edge.
Traditional serverless (like Vercel or standard AWS Lambda) is ideal for tasks requiring a full Node.js environment, heavy database interactions, and complex backend logic. The pros are full compatibility and generous execution times. The cons are regional latency and cold starts.
Edge computing (like Cloudflare Workers), on the other hand, runs your code in data centers all over the world. The pros are near-zero latency globally and no cold starts. The cons are smaller bundle size limits and the lack of native Node.js APIs, though modern Web APIs cover most use cases.
My decision was to keep the core SaaS boilerplate—Next.js 14+ App Router, Stripe subscription billing, and NextAuth.js—on traditional serverless. But I moved the auxiliary tools, specifically the SEO checker, to Cloudflare Workers.
To illustrate the contrast, here is how the core billing route looks in the traditional Next.js App Router. It handles the heavy lifting of creating a Stripe checkout session, which requires a full Node.js environment and secure server-side 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 });
}
This separation of concerns is the key takeaway. You use traditional serverless for secure, database-heavy, or Node-dependent tasks like Stripe billing. You use the edge for fast, I/O-bound, globally distributed tasks.
Here is what the edge worker for the SEO analysis looks like. Instead of using a heavy DOM parser, it fetches the HTML and uses simple regex to extract the necessary metadata.
// 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' },
});
},
};
By using the cf object in the fetch request, we can leverage Cloudflare's cache at the edge, drastically reducing the load on the target server and speeding up subsequent requests. Because this runs on Cloudflare Workers, a user in Tokyo and a user in New York get the exact same low-latency experience. The regex approach keeps the bundle size tiny, which is crucial for edge environments.
Practical Results
Splitting the architecture yielded immediate benefits. The edge worker handles the SEO fetching and parsing globally with virtually no latency. Meanwhile, the core Next.js application remains incredibly responsive because it is no longer bogged down by heavy HTML parsing tasks.
What surprised me was how smooth the developer experience remained. Even though I was writing code for two different environments, using a TypeScript-first approach meant I had full type safety across both the Next.js App Router and the Cloudflare Workers.
If I could change one thing, I would have abstracted the edge routing more cleanly from day one. Tacking it on later meant I had to refactor some of my internal API calls. Next time, I will design the boundary between the core app and the edge tools much earlier in the planning phase.
Try It Yourself
If you are building a SaaS and want to see how this hybrid architecture looks in practice, I put together a production-ready starter kit called SaaSLaunch. It includes the Next.js core, Stripe billing, and the edge worker setup we just discussed.
You can actually test the edge SEO tool directly right now. We have a free SEO Checker available at https://saaslaunch.dev/free-tools/seo-checker to see how fast the edge analysis runs.
Full disclosure: I'm the developer of SaaSLaunch. I built this because I needed it myself.
Top comments (0)