Elhadi.
← All posts
4 min readAI-written · in Suhaib Elhadi's voice

Next.js or FastAPI? How I actually pick a backend as a solo builder

This update was drafted on a schedule by the AI I build with, from real project notes — part of the vibecoding experiment this blog documents.

Someone asked me how I pick a backend, and I gave a much longer answer than the question deserved, so let me write the short one down.

Next.js vs FastAPI, for a solo builder, is almost never a performance question. Both are fast enough. You are not going to be the person whose product died because of a framework's request throughput — you're going to be the person whose product died because you never finished it. So the real question is a different one: where does the hard part of this app actually live?

That's the whole rule. Everything below is just me explaining it.

When the app is the product, use Next.js

If the interesting part of the thing is the interface — the screens, the flow, the way it feels to use — and the data underneath is mostly reading and writing records, Next.js wins and it isn't close. One repo. One language. One deploy. The API routes sit right next to the pages that call them, and you never once think about how the frontend talks to the backend, because there isn't a gap for that thought to fall into.

That's most of what I've built. Fundability, CommunityHQ, ClipForge, CreatorLens — all Next.js. And Fundability is interesting as a test of the rule, because it has a real rule engine in it: Metro 2 and FCRA logic, the kind of thing that sounds like it wants to be Python. I kept it in TypeScript anyway. Not out of loyalty — because when I looked hard at what that engine really does, it reads a document, applies rules, and generates another document. It's text in, text out. There's no numerical library I'm missing, no ecosystem I'm cut off from. Splitting the stack to run it would've bought me nothing and cost me a second deploy target forever.

That's the question to ask, by the way. Not "is this logic complicated." Complicated logic is fine in any language. It's "does this logic need an ecosystem I can only get somewhere else."

When there's a real engine underneath, use Python

Sometimes the answer to that question is yes. App Marketing OS is FastAPI on SQLite, and it's Python because the five modules underneath are genuinely doing analysis — that's Python's home turf and pretending otherwise would've been stubbornness. Confluence is the clearest case: a Python analysis engine scoring market setups, with a Next.js frontend on top. The engine came first and the interface came after, which tells you which part was the actual project.

The tell is pretty reliable. If you'd still want the core to exist as a thing you could run from a terminal — no UI, just feed it data and get results — that core wants to be Python, and the web app is a window onto it. If the thing makes no sense without its screens, it's a Next.js app and any "engine" talk is you flattering your CRUD.

What splitting the stack actually costs

Here's the part I'd want a friend to hear before they go two-service. The split is never just "a second folder." It's two dependency worlds to keep alive, two deploy targets, two places for auth to be subtly wrong, and CORS — which will absolutely eat an afternoon at some point, and it'll be an afternoon you didn't budget. None of that is hard. All of it is forever. And forever is the expensive column.

So I don't split for elegance. I split when the core genuinely lives somewhere else. Confluence earned it. Fundability didn't.

The thing AI actually changed here

Worth saying, because I think a lot of people are still choosing on the old criteria. The old tiebreaker was fluency: pick the language you write fastest in. That mattered enormously, and it barely matters now. Claude writes decent Python and decent TypeScript, and the gap between "the language I know" and "the language that fits" has closed to something close to nothing on the writing side.

But — and this is the bit people skip — it closed on writing, not on owning. Producing the code got cheap. Reading it when it breaks, debugging it at 2am, knowing why the thing behaves oddly on a Tuesday: still yours, still expensive, still the actual job. So my decision moved off "what can I type fastest" and onto "what do I want to be on the hook for." Which usually argues for fewer moving pieces, not more. One deploy, one runtime, one place to look when something's wrong.

Where my own rule breaks

It breaks on long-running work, and I'll be honest that this is where I've made the most mess.

"One repo, one deploy" is lovely right up until you have a job that runs longer than a web request has any business running. Serverless functions have time limits, and a heavy background loop will find them. At that point the neat single-deploy story starts leaking — you end up reaching for cron, or a queue, or a worker that lives somewhere else — and you're back to operating multiple things regardless of which language you picked. That's not a Next.js problem or a FastAPI problem. It's a "this workload isn't request-shaped" problem, and no framework choice saves you from it.

The lesson I actually took: the question isn't only what language, it's what shape is the work. Request-shaped work is easy anywhere. Long-running work is its own decision, and if I'd asked that question first instead of last I'd have saved myself a couple of rough weeks.

So: does the hard part live in the interface or in an engine? Is the work request-shaped or not? Answer those two honestly and the framework picks itself. Everything after that is preference, and preference is a much cheaper thing to be wrong about.