The choices I made building a 100k-user AI consumer app solo. FastAPI vs Node, prompt orchestration, streaming, and the bits that broke first.
/ 01The brief, and why I said yes.
When the DreamyBot founder reached out, the brief was simple. Ship an AI consumer app for iOS and Android, charge for it, and don't take ninety days to do it. The catch: no backend engineer. No other engineer. Just me, a designer, and a roadmap that wanted Claude in five different places.
I said yes because this is exactly the brief I built up a Node and Python practice next to my Flutter work for. Founders keep asking for one person who can ship the app, the API, and the AI layer. The honest answer is that you can have that person (I'm one of them), but you need to know what tradeoffs come with it.
/ 02FastAPI vs Node, and why I picked both.
Node runs everything that talks to RevenueCat, Stripe, and Firebase. The SDKs are first-class, the team can rotate engineers in and out without retraining, and the deploy story on Cloud Run is dull in a good way. FastAPI runs everything that talks to an LLM. Python's tooling around prompts, embeddings, and evals is two years ahead of Node's, and the streaming primitives in fastapi.responses are quietly excellent.
The rule I follow: pick the language with the better SDK for the third party you're spending most of the request inside.
/ 03Streaming responses without breaking the mobile client.
Server-sent events from FastAPI into a Flutter StreamBuilder turn out to be the boring, correct answer. The first version of the app tried websockets and I spent two weeks on edge cases that SSE doesn't have. If you're shipping a mobile AI app and you don't need bidirectional, don't reach for websockets.
@router.post("/chat/stream")
async def chat_stream(req: ChatReq):
async def gen():
async for chunk in client.messages.stream(...):
yield f"data: {json.dumps(chunk)}\n\n"
return StreamingResponse(gen(), media_type="text/event-stream")/ 04What I tore out.
The first prompt orchestration layer was a beautiful state machine, and it was wrong. Users don't move through your state machine. They move sideways through it. I replaced it with a thin router that picks one of four prompts based on the most recent message and a small piece of session memory. A thousand lines shorter, and the answers got better.
/ 05What I'd do again.
- Prompts versioned in the repo, never in a database. Roll back is
git revert. - One eval suite per prompt. Run it on every PR.
- RevenueCat from day one, even if you don't think you'll monetize for a quarter.
- Log every model response with a hash of the system prompt. You will need this in week six.
If you're a founder shipping an AI mobile app and you want to talk through any of this, book a call. Thirty minutes, no pitch.