Prompt-to-Image Generator
A Django app that turns a text prompt into an image via Stability.ai.
A prompt goes in, an image comes back, and the API key never touches the browser — the backend owns the credential, the request, and the response parsing.
- Django backend integrating the Stability.ai generation API.
- API key held server-side; the client never sees it and cannot call the provider directly.
- JSON response parsed and the encoded image returned to the page in one flow.
The problem
The naive version of this app calls the generation API from JavaScript, which means shipping a paid API key to every visitor. Anyone can read it, and anyone can spend your credits. Putting a backend in the middle is not architecture for its own sake — it is the only place a secret can live.
Constraints
- A paid, rate-limited third-party API — every request costs real money.
- Generation takes seconds, not milliseconds, so the request cannot feel broken while waiting.
- The credential must never reach the client.
How it fits together
Prompt form
browser
Django view
holds the API key
Stability.ai
generation endpoint
Parse JSON
extract encoded image
Rendered image
Decisions and what they cost
Every one of these had a cheaper or more obvious alternative. These are the reasons I didn't take them.
Proxy the API through Django rather than calling it from the client.
Instead ofDirect browser-to-provider calls with a restricted key.
There is no such thing as a client-side secret. Proxying also puts the one place that spends money under my control, which is where any future rate-limiting or caching would have to go anyway.
Server-side response parsing rather than handing raw JSON to the page.
Instead ofReturn the provider's response verbatim and parse it in JavaScript.
It keeps the provider's response shape an implementation detail. If the API changes its envelope, one Django view changes and the frontend does not — and the frontend never learns anything about the provider it does not need to know.
What broke
A multi-second generation with no feedback reads as a broken page, and users retry — which costs money on a paid API.
FixMade the pending state explicit in the interface so the wait is legible as work rather than as failure.
What I'd do next
The interesting version of this project is the one I did not build: a job queue so generation is asynchronous, a cache so identical prompts do not pay twice, and per-user rate limiting. All three are the same realization — the expensive external call is the thing the architecture should be organized around.