Static Sites, Web Apps, and APIs
Table of Contents
- Same HTTP, Different Servers
- Static Sites
- Server-Rendered Pages
- Single-Page Apps
- Mixing the Three
- APIs: HTTP for Programs
- CORS, Briefly
- Frontend and Backend
- Try It Yourself
1. Same HTTP, Different Servers
Everything in this series so far is true for every website: URL, DNS, connection, request, response, rendering, cookies, caching. What changes from site to site is what the server does between receiving a request and sending the response.
There are three main answers, and you'll hear developers argue about them constantly:
| Kind | Where the HTML is made | When |
|---|---|---|
| Static site | Already made, sitting in files | Before anyone visits |
| Server-rendered | On the server | Fresh, for every request |
| Single-page app (SPA) | In the browser, by JavaScript | After the page loads |
2. Static Sites
A static site is a folder of ready-made files. When a request arrives, the server finds the matching file and sends it back. That's all. No code runs, no database is asked anything.
GET /my-site/about.html ──▶ server reads about.html from disk ──▶ 200 + the file
Your GitHub Pages site is static: you wrote the HTML files, and GitHub's servers simply hand them out.
Static sites are:
- Fast — nothing to compute, and every file can be cached by a CDN.
- Cheap — often free. GitHub Pages, Cloudflare Pages, and Netlify host them at no cost.
- Secure — there's no server code or database to attack.
- Simple — nothing to crash at 3 a.m.
The limit: every visitor sees the same files. A static site can't show "Hi Dara" to Dara and "Hi Sokha" to Sokha, or save a comment you type — not on its own.
Static doesn't mean hand-written, either. Many static sites are generated by a tool that turns Markdown or data into HTML files once, at build time. This website is one. Every tutorial, including this chapter, is a Markdown file that Next.js turned into a plain HTML file when the site was built. There's no server program running for it, and no database — which is why it costs almost nothing to host.
3. Server-Rendered Pages
A server-rendered site runs code for every request. The server reads the request (path, cookies, query), asks a database for what it needs, builds HTML from it, and sends it back.
GET /dashboard
Cookie: session=a8f3…
│
▼
server code: who is a8f3…? → Dara
get Dara's orders from the database
build the HTML: "Hi Dara! You have 3 orders…"
│
▼
200 + HTML made just for Dara
That's how you get pages that are personal and up to date: your inbox, your bank balance, your orders. The browser still receives ordinary HTML — it can't even tell whether a page came from a file or was built a millisecond ago.
The cost is a server that's always running, a database, and more things
that can break — the 500s and 502s from chapter 6. Classic
examples are PHP sites like WordPress, and frameworks like Django,
Ruby on Rails, and Laravel.
4. Single-Page Apps
A single-page app moves the work into the browser. The server sends an almost empty HTML page and a big JavaScript file:
<body>
<div id="root"></div>
<script src="/app.81d0e47f.js"></script>
</body>
The JavaScript then builds the whole page itself, in the DOM. When it
needs data, it uses fetch() (chapter 7) to ask a server for it. When
you click a link, it doesn't load a new page — it changes the DOM and
updates the address bar itself.
That's what you saw in chapter 7 when view source and the Elements tab looked completely different.
SPAs feel smooth, like an app on your phone: no white flash between pages, instant reactions to clicks. Gmail, Facebook, and Trello work this way. The costs are a big JavaScript download before anything appears, and pages that search engines and link previews may have trouble reading, because the HTML they receive is empty. React became famous for building SPAs.
5. Mixing the Three
In practice, modern frameworks mix all three — even page by page. With Next.js, which you'll meet on the Frontend track, one site can have:
- A static blog and landing pages, built ahead of time.
- Server-rendered dashboard pages that need fresh data.
- Pages that load as HTML first and then behave like an SPA, once JavaScript takes over in the browser.
That's why the question isn't "which one is best?" but "which one fits this page?" A portfolio that changes once a month should be static. A bank account page must be server-rendered or fetch its data. You'll get a feel for it with practice.
6. APIs: HTTP for Programs
So far, responses have been HTML: meant for a browser to show to a person. But HTTP can carry any kind of data. When the client is a program — an SPA, a mobile app, another server — it doesn't want a web page. It wants data.
An API (Application Programming Interface) is a server that answers HTTP requests with data instead of pages, usually as JSON:
curl https://api.github.com/users/octocat
{
"login": "octocat",
"name": "The Octocat",
"public_repos": 8,
"followers": 21000,
"created_at": "2011-01-25T18:44:36Z"
}
Same request, same response, same status codes, same headers — just
Content-Type: application/json instead of text/html. Everything you
learned in chapters 5 and 6 applies directly.
Most APIs follow a style called REST, which uses the URL for what and the method for what to do:
| Request | Meaning | Typical response |
|---|---|---|
GET /users | List users | 200 + a JSON list |
GET /users/42 | Get user 42 | 200, or 404 if there's no user 42 |
POST /users | Create a user | 201 Created |
PATCH /users/42 | Update part of user 42 | 200 |
DELETE /users/42 | Delete user 42 | 204 No Content |
This is exactly what you build in the NestJS Crash Course. And the ABA PayWay and Bakong KHQR tutorials are about calling someone else's API — a bank's — from your own server.
The mobile app on your phone, the banking app, the food delivery app — underneath, they're almost all clients sending HTTP requests to APIs. That's why this series sits at the very start of every track on the roadmap.
7. CORS, Briefly
Sooner or later, every frontend developer meets an error like this in the browser console:
Access to fetch at 'https://api.example.com/data' from origin
'http://localhost:3000' has been blocked by CORS policy
Here's what's going on. An origin is the scheme + host + port of a
page: http://localhost:3000 and https://api.example.com are
different origins. For safety, browsers don't let JavaScript on one
origin read responses from another origin — otherwise, any website you
visited could quietly read your email from another tab, using your
cookies.
CORS — Cross-Origin Resource Sharing — is how a server says "it's okay, I allow that origin", with a response header:
Access-Control-Allow-Origin: http://localhost:3000
For some requests, the browser first sends an OPTIONS request (the
method from chapter 5) — called a preflight — to ask permission
before sending the real one.
Two things to remember:
- CORS is enforced by the browser, not the server. That's why the
same request works perfectly in
curland fails in the browser. - The fix is on the server that owns the API: it has to send the right headers. You can't fix a CORS error from the frontend.
You'll set this up yourself on the Backend track.
8. Frontend and Backend
You can now put a precise meaning on two words that appear in almost every job ad:
- Frontend is everything that happens in the browser: the HTML,
CSS, and JavaScript from chapter 7 — layout, interaction, calling
APIs with
fetch(). - Backend is everything that happens on the server: receiving requests, checking cookies or tokens, talking to the database, and sending responses — chapters 5, 6, and 8 from the server's side.
They meet at exactly one place: the HTTP request and response. A frontend developer and a backend developer who both understand this series can always work out where a problem is. Open the Network tab, find the request, and ask: did the frontend send the right thing? Did the backend send back the right answer?
9. Try It Yourself
-
Call a real API. Run:
curl https://api.github.com/users/<your-username> curl -I https://api.github.com/users/<your-username>Find your
public_reposcount. In the headers, findcontent-typeand thex-ratelimit-remainingheader. What do you think it counts? (Remember 429 from chapter 6.) -
Get a 404 from an API. Ask for a user that doesn't exist, like
curl -i https://api.github.com/users/this-user-does-not-exist-123(-ishows the headers and the body). APIs send errors as JSON too. -
Classify sites. For three sites you use, open view source and the Elements tab, and look at the Fetch/XHR filter in the Network tab. Is each one static, server-rendered, an SPA, or a mix?
-
Watch an app use its API. Open a site you're logged in to (like GitHub or YouTube) with the Fetch/XHR filter on, then click around. Click one of the requests and look at its Response tab. It's JSON — the frontend talking to the backend.
-
See CORS headers. Run
curl -I https://api.github.com/users/octocatand look foraccess-control-allow-origin. What does its value mean?
In the final chapter, we'll put everything together and trace one URL, from the moment you press Enter to the moment the page is on your screen.
Buy Me a Coffee
Found this tutorial helpful? Consider buying me a coffee to support more free content!