slopware 101 · step 5 of 35
What should the browser be allowed to do?
by Damla Su Bilge · 4 min read
The last chapter ended on the difference between a page on your laptop and one on a machine strangers can reach. Once there is a machine on the other end, the next question is what belongs on it. Why can you not write everything in the frontend and let the browser talk straight to the database?
If that connection needs a privileged database password, the browser would need the password too. Anything you send to the browser is available to the person holding it. Managed data APIs provide another design, which we will reach after drawing that boundary.
Two machines
Frontend is the code the browser downloads and runs on the user's machine. Backend is the code that runs on a machine the user never touches. The screen you look at and the state the page keeps while you use it are on the frontend side. Calculations and interface behavior can run in either place. Privileged database connections and authoritative permission decisions belong on a service the visitor cannot rewrite. Frontend is the dining room and the menu, backend is the kitchen. The customer does not walk into the kitchen.
Requests, methods and status codes
The browser and the server talk over HTTP. The browser sends a request and the server sends back a response. An API is the list of requests your server agrees to answer. For a small notebook app it might look like this:
GET /api/notes list my notes
POST /api/notes create a note
PUT /api/notes/42 replace note 42
DELETE /api/notes/42 delete note 42
The method is a statement of intent. GET reads, POST creates or submits, PUT replaces, DELETE removes. The HTTP specification describes these meanings and other software relies on them: a browser may retry a GET, a cache may keep a copy of one. They are still conventions, not guarantees. Nothing stops a server from deleting a note when it receives a GET. The method says what the server promised, and your code keeps that promise or does not. Write each handler to match its method, and never assume a request is harmless because of the word in front of it.
Each response carries a status code. 200 reports a successful HTTP response; the response body still needs to make sense for the operation. 404 says the resource was not found, or is being concealed. 500 reports an unexpected server-side failure. 401 says valid authentication credentials are missing. 403 says the server refuses the request; it need not establish who you are first.
Open the Network panel on any site and click a request. The method, the status, the headers and the response are all there. The destination shows which service was asked. A cached response or service worker may answer without a new network trip.
The trust boundary
The trust boundary is the line between the machine you control and the machine you do not. Everything the browser downloaded sits on the far side, and that has three consequences.
A secret key inside a React component ships to the user's machine, and anyone who opens DevTools can read it. A permission check written only in the frontend can be skipped, because the user can edit or simply bypass the code that runs it. A price computed in the frontend can be changed, because the user edits the number and your server receives whatever the browser sent.
Say the notebook app hides other people's notes by not showing a button. Nothing stops me from sending GET /api/notes/42 myself, from the console or a terminal. Whatever the server answers is what I get. The check has to live where the data lives.
Moving the key
So the secret moves to the machine you control. The browser calls an endpoint you own, one address on your own server. Your server holds the key, does the work, and sends back only the result. Whatever arrives from the browser is checked again on the server, because a value that travelled through the user's machine could have been changed there. One backend can then feed the web app and the phone app you write later.
In stitchu, the hosted photo-reading flow sends a garment photo or description to a reading service. It then checks that the response matches that input and compiles the returned candidate in the browser. Different stages belong on different sides of the boundary; calling the whole product frontend or backend hides those decisions.
What managed services change
Services like Supabase and Firebase let the browser talk to their data API directly, and their documentation tells you to put a key in your frontend code. That is legitimate. That key is designed to be public. It identifies the project and permits whatever access the service's public role and rules allow. It does not establish which person is using the app.
The authority lives in rules that run on their servers: row level security policies in Supabase, security rules in Firebase. A rule that runs on their machine is a rule the user cannot edit. The mistake is not the public key in the browser. It is leaving the rules open, so that the public key reaches everything. The same services also give you admin or service keys that bypass those rules, and those are secrets that never go near the browser.
It is the same boundary with a different kitchen.
One request
Open the Network panel on your own site and read one request your page sent: its method, its status and its response. Then answer one question about it. Which machine decided whether you were allowed to have that response?
Further reading: MDN: client and server.


