I'm sharing a look at my own project, Journal Prompts, a web app that helps someone pick a writing question and start a journal entry. The implementation uses Next.js, React, Cloudflare Workers, and Drizzle with Neon PostgreSQL. The useful design question is where a model call belongs. A person opening a journal may only need one existing question and somewhere to write. That gives the application two distinct paths: selecting a curated prompt and generating a new one. 1. Keep prompt selection separate from generation The curated library stores questions with fields such as mood, writing direction, and topic. The matcher uses those fields to select existing questions. A request for a gratitude prompt can stay within that collection, while a general request can use the broader library. This path does not need an AI provider request. Its tradeoff is editorial work: someone still needs to review the questions and how they are categorized. Matching existing text also has a smaller range of responses than generating new text. AI generation is a separate server action. It validates the input, uses the authenticated session, checks the account's allowance, and calls OpenRouter. The response includes the generated question and remaining usage information. Keeping this separate makes the dependency visible: provider failures affect the generation action rather than turning the prompt collections into empty pages. 2. Treat writing storage as a product boundary The editor supports local guest writing and account-based saving. These serve different expectations. Writing mode Storage boundary What the interface needs to explain Guest writing Browser localStorage Entries stay on that browser and can be removed when browser data is cleared. Account-based saving Server persistence through Drizzle and PostgreSQL Saving depends on authentication and a successful server response. A writing field containing text is not, by itself, evidence that the text reached the server. Likewise, being signed in does not make a local draft a completed cloud save. The interface needs to keep those states understandable. This is a useful distinction for other small web tools too: a local-first starting experience and an account-backed service can coexist, but the storage boundary should be explicit. 3. Put generation checks on the server The browser can display a remaining allowance, but it is not the authority for that allowance. The generation action checks the current account and recorded usage before calling the provider. Input validation also happens at that boundary. The practical tradeoff is another database-dependent step in the generated-prompt path. In exchange, the client does not need to carry provider credentials or decide which account benefits apply. Curated selection remains a separate operation. 4. Let topic pages work as ordinary web pages The journal prompt generator page combines explanatory content with the interactive finder. Other pages focus on daily reflection, gratitude, morning writing, and related topics. They contain ordinary links to related collections and the finder. These pages give someone a useful starting point before they interact with the application. The same links also provide a crawlable structure: a topic page can lead to a related topic, a technique, or the main tool. OpenNext adapts the Next.js application for Cloudflare Workers. Database access and generation remain server-side concerns, while React handles the interactive selection and writing experience. For a small AI-assisted product, it helps to draw these boundaries before adding more features: what can work from reviewed content, what requires a model, and when a local interaction becomes a server-backed promise. Those decisions shape both the interface and the failure cases it needs to handle.