How real PrivateAI apps stay organized as they grow — one site, one AI brain, one memory, working together.
The single-file chat app from Tutorial 4 works great — right up until your app needs to remember a conversation between visits, let people upload images, or generate a structured summary on demand. At that point, one giant file holding the page, the AI logic, and the storage logic all at once starts getting hard to follow and hard to fix.
The fix isn’t more complexity — it’s splitting one Worker into three, each with a single job. This is the exact pattern behind PrivateAI Tutoring and every journal-style app in this series.
Serves the page people actually see and use. Holds the HTML, CSS, and front-end JavaScript — the same pattern from Tutorial 4.
Holds the system prompt and talks to the AI model. Receives a message, returns a reply. Nothing else.
Saves and recalls data — conversation history, uploaded images, anything that needs to persist between visits.
💡 Notice each Worker has one job. When something breaks, you immediately know where to look — if replies are wrong, check the Chat Worker; if history isn’t saving, check the Storage Worker. That focus is the entire benefit of splitting things up.
This is simpler than it sounds — it’s the exact same fetch() call from Tutorial 4, just pointed at a different Worker’s URL instead of your own /chat route:
const response = await fetch("https://my-chat-worker.yoursubdomain.workers.dev", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ message: userMessage, history: previousMessages }) }); const data = await response.json();
Your Main Site Worker calls this from inside its own /chat handler, then passes the reply straight back to the visitor’s browser. The visitor never knows there are three separate Workers involved — it all feels like one app.
Workers KV is a simple key-value storage system — think of it as labeled folders where you can save a piece of text and retrieve it later by name. It’s how the Storage Worker remembers a conversation between visits.
In your Storage Worker’s Bindings tab, add a KV Namespace binding. Create a new namespace if you don’t have one, and name the variable something like JOURNAL_KV.
// Save data await env.JOURNAL_KV.put("session:abc123", JSON.stringify(messages)); // Retrieve it later const saved = await env.JOURNAL_KV.get("session:abc123"); const messages = saved ? JSON.parse(saved) : [];
That’s the entire mechanism — put to save, get to retrieve. The tricky part is the key, the label you use to find the data again — which is where sessions come in.
A Worker has no idea who’s visiting — every request looks like a stranger unless you give each visitor a unique ID and ask their browser to remember it. The standard trick: generate a random ID the first time someone visits, and save it in the browser’s localStorage so it’s still there next time.
function getOrCreateSessionId() { let id = localStorage.getItem("my_session_id"); if (!id) { id = "s_" + Date.now() + "_" + Math.random().toString(36).slice(2, 8); localStorage.setItem("my_session_id", id); } return id; }
This session ID becomes the KV key. The same visitor, on the same device, always gets the same ID back — so env.JOURNAL_KV.get("session:" + sessionId) always finds their own conversation, and no one else’s.
⚠️ localStorage is tied to one browser on one device. The same visitor on their phone and their laptop will get two different session IDs, and two separate conversations. That’s fine for a journal or practice tool, but worth knowing if you ever wonder why "my history disappeared" on a different device.
A simple rule of thumb: stay with the single-file Worker from Tutorial 4 until you specifically need one of these things — remembering a conversation between visits, storing uploaded files, or generating an on-demand summary of a whole session. The moment you need persistent memory, split into the three-Worker pattern. Until then, the simpler single file is genuinely the better choice — don’t add architecture you don’t need yet.
Six tutorials ago you didn’t have a domain. Now you understand Cloudflare Workers, AI integration, chat app architecture, PWAs, and the Google Play Store. Keep building — and keep your own work private along the way.
Open Your Private Dev Journal →