# Frontend vs. backend: what runs in the browser and what runs on the server

> What runs in the browser and what runs on the server, how to tell them apart in an app built with AI and why that boundary decides where each rule must live.

*Tags: ai, frontend, backend, architecture, ux-for-ai*

---


> [!info] Quick Definition
> **The frontend** is the code that runs in the person's browser and decides how the page looks and behaves. **The backend** is the code that runs on a server and decides which data is returned, who may see it and what is stored. The boundary between them is the trust boundary: what runs in the browser is controlled by whoever uses it, not by you.


## What do technical sources say about each side?

"Frontend" and "backend" are everyday terms; MDN's documentation prefers to talk about client-side and server-side code. According to MDN, code that runs in the browser is concerned with improving the appearance and behavior of the page: selecting and styling interface components, creating layouts, navigation and form validation. It runs inside the browser and has little or no access to the operating system. Server-side programming, by contrast, is mostly about choosing which content is returned to the browser in response to each request. That code validates the data and requests that arrive, uses databases to store and retrieve information and keeps sessions, the mechanism a server uses to remember a person and respond differently depending on who they are ([MDN, Introduction to the server side](https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Server-side/First_steps/Introduction)).

One detail confuses beginners: the language does not define the side. MDN notes that JavaScript runs in the browser but can also be a server-side language, for example in Node.js ([MDN, What is JavaScript?](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Scripting/What_is_JavaScript)). What matters is where it runs.

## Where does each piece run in an app generated with AI?

Vibe coding tools generate projects that look like a single block, but each file runs in a different place. These signals help:

| Signal | Probably browser | Probably server |
|---|---|---|
| Pages, styles and interactive components | Yes | No |
| Server routes, functions or endpoints | No | Yes |
| Direct connection to the database | No | Yes, or through an API |
| Private keys (payments, AI models) | Never | Yes, in environment variables |
| Per-user permission checks | Only as visual support | Yes, mandatory |

Current frameworks blur the line inside a single project. In Next.js, for example, pages and layouts are Server Components by default, which can fetch data and use keys without exposing them to the client. Files marked with `"use client"` (and what they import) become part of the code sent to the browser, and are reserved for when you need state, events such as a click or browser APIs ([Next.js, Server and Client Components](https://nextjs.org/docs/app/getting-started/server-and-client-components)). If you ask the AI for a button that saves data, ask it which of the two kinds of file it put it in.

There is a frequent trap with environment variables. In Next.js, only variables with the `NEXT_PUBLIC_` prefix are included in the code the browser receives; the rest are replaced with an empty string ([Next.js, Server and Client Components](https://nextjs.org/docs/app/getting-started/server-and-client-components)). In Vite, those starting with `VITE_` are exposed in client code, and its documentation warns that they should not contain sensitive information such as API keys ([Vite, Env variables and modes](https://vite.dev/guide/env-and-mode)). So a private key with one of those prefixes is the same as publishing it. Which keys can go in the browser is covered in [[environment-variables-and-secrets|environment variables and secrets]].

## What happens when the app talks straight to the database?

An app can use a data service such as Supabase and query the database from the browser, with no server of its own. That does not remove the backend: it moves it into the service, and security depends on its configuration. Supabase's documentation warns that a table in an exposed schema without Row Level Security (RLS) is readable and writable by any role with a grant on it. With RLS enabled, no data is accessible through the API with a publishable key until policies are created, and secret keys must never be used in the browser ([Supabase, Row Level Security](https://supabase.com/docs/guides/database/postgres/row-level-security)). How those rules are written is explained in [[databases-for-designers|databases for designers]].

## What should live on each side?

A practical rule: the browser shows and asks; the server decides and stores.

- **In the browser:** the design, the messages, the screen's state and validations that improve the experience, such as warning that an email is missing its @ before sending it.
- **On the server:** business rules, permissions, keys, writing to the database and the final validation of everything that arrives.

Browser validation helps the person, but protects nothing: whoever sends the request without going through the screen skips it. The one that counts is the server's or the database's. The [[apis-for-designers|API]] is the channel the two sides use to talk, and [[deploy-for-designers|deploying]] is publishing both parts.

## Why does it matter to a designer who works with AI?

Because every design decision that depends on a permission must be backed on the server. Hiding a "Delete" button from someone without permission is experience. If the button disappears but the server still accepts the request, it is a hole. The useful question for you is: who enforces this rule? You can ask the AI the same thing whenever it hands you a screen.

## Example

Illustrative example: a designer has an app for client requests. The screen hides the "Approve" button from anyone who is not a manager, and that is what shows up in tests. On review, she finds that the server function that approves requests does not check the caller's role, so anyone with a well-formed request could approve. She asks for the rule to be verified on the server, adds a test with an account without permissions trying to approve, and only then considers the screen done. The hidden button was experience; the server check is the security.

## How to apply it tomorrow

1. List five important rules in your app: who can do what, which data is private, what costs money. For each one, note whether the browser or the server enforces it.
2. If a rule is enforced only by the browser, ask the AI to repeat it on the server or in the database, and test it with an account that should not be able to do it.
3. Look for keys in variables with a public prefix (`NEXT_PUBLIC_`, `VITE_`) and in files the browser downloads. If there is a private one, move it to the server before anything else.
4. When you describe a flow to the AI, say which side each part lives on: "this check runs on the server, not in the component". Most architecture mistakes come from an ambiguous sentence.

If the tool you use builds the app end to end, [[what-is-vibe-coding|what is vibe coding]] explains why this boundary is worth checking.

## Common mistakes

- Believing that hiding a button restricts an action.
- Putting permission checks only in the browser.
- Keeping private keys in files or variables the browser downloads.
- Thinking an app "without a server" has no backend: the service it uses is one.
- Confusing format validation (an email with an @) with permission validation.
- Assuming JavaScript always runs in the browser.

## Sources

- [MDN, Introduction to the server side](https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Server-side/First_steps/Introduction): client vs. server, validation, databases and sessions.
- [MDN, What is JavaScript?](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Scripting/What_is_JavaScript): client-side and server-side code, JavaScript in Node.js.
- [Next.js, Server and Client Components](https://nextjs.org/docs/app/getting-started/server-and-client-components): Server Components by default, `"use client"` and `NEXT_PUBLIC_` variables.
- [Vite, Env variables and modes](https://vite.dev/guide/env-and-mode): `VITE_` variables exposed in client code.
- [Supabase, Row Level Security](https://supabase.com/docs/guides/database/postgres/row-level-security): the database enforces access; secret keys do not belong in the browser.


---

Source: https://www.fernandoux.com/en/wiki/ai/frontend-vs-backend/
