Same food delivery app. Same customers. Same Azure SQL database. Built two ways: an insecure architecture and a secure one.
On launch day, both versions work perfectly. That’s the whole problem — nothing tells you which one you built until something goes wrong.
What do the two architectures look like?
Before — the app talks straight to the database. It carries a connection string and queries Azure SQL itself. It’s fast, it’s simple, and it works, which is exactly why it ships.
After — the app goes through Azure Front Door to your own API, and your API talks to a database that has no public address at all.

Where the two designs differ
At six moments. For the first, you genuinely can’t tell them apart. By the sixth, one of them can’t be fixed without rebuilding it.
| Moment | Before (app → database) | After (app → API → private SQL) |
|---|---|---|
| 1. A real customer orders | 40ms | 52ms — nobody notices |
| 2. Someone unzips the app | Finds a connection string with a password | Finds a token that expires in an hour, for one user |
| 3. He connects to the database directly | It answers — he has valid credentials | No address to connect to |
| 4. He asks for someone else’s order | Nowhere to put an owner check | API filters by the user id in the token |
| 5. You rotate the secret | New build, store review, broken old installs | One command, nobody notices |
| 6. You need to change anything | An app release, measured in days | A server change, measured in minutes |
Moment 3 is the one that surprises people. From the database’s point of view, the attacker’s login is completely legitimate — he’s using the same credentials as every installed copy. In the “after” design, publicNetworkAccess is Disabled and the server isn’t on the internet at all.
Moment 4 is the bug that sits at number one on the OWASP API Security Top 10. The fix is a WHERE clause in your API, covered in the full request-flow post. In the “before” design there’s no API, so there’s nowhere to write it.
Moment 5 gets its own post: what really happens when you hardcode a key.
The real cost of the insecure design
Response time. In the “before” design, every security fix is an app release, so your response to any incident is measured in days and gated by a store reviewer you don’t control. In the “after” design, every fix is a server-side change you can ship before lunch.
Why the first design is so common
It isn’t laziness. Direct database access is honestly the fastest path to a working prototype, mobile SDKs make it easy, and nothing about it feels wrong while you’re building. The bill arrives later, and it arrives as an architecture change instead of a bug fix.
How do you migrate without a big-bang rewrite?
You don’t have to do it in one go:
- Stand up an API in front, even if at first it only proxies the same queries.
- Ship an app version that calls the API. Leave the old path working.
- Add authentication, then owner checks.
- When enough users have updated, turn off public network access on the database.
Key takeaways
- Both architectures behave the same for a legitimate request; they only diverge at failure and change moments.
- Direct database access is a reasonable prototype — the mistake is launching with it.
- Migrate in stages: proxy first, add auth and owner checks, close the database’s public access last.
- The ongoing cost of the insecure design is response time: every fix waits on an app store review.
- The secure design costs more up front; the insecure one costs more later, repeatedly, at the worst possible moment.
Official documentation
Official documentation: Azure API Management key concepts and managed identities.
