Backend from First Principles

Topic 1 of 4

HTTP & the request lifecycle

What is a backend?

Interview answer

The backend is the central server that stores data for everyone, keeps secrets like the database password, and applies the same rules to every user. The frontend runs on the user's own device and forgets everything when the tab closes.

The Twitter example

Frontend only

Your browserFriendYour phoneYour browser holds—

With a backend

Your browserBackendFriend / phoneBackend holds—

Press ▶ Play or “Next step”. Arrow keys work too.

Debug view

Frontend only
Step through the diagram to see requests and responses.
With a backend
Step through the diagram to see requests and responses.

Glossary

Interview answer

JavaScript is just the language. V8 is the engine that runs it. The browser and Node.js are runtimes: V8 plus extra tools (DOM and fetch in the browser; files and networking in Node). Express is a framework on top of Node for routes and middleware.

TermMeaning
HTTP / HTTPSClient sends a request, server sends a response. HTTPS is the same protocol, encrypted in transit.
WebSocketsOne connection stays open, and both sides can send messages at any time. Chat, live dashboards, games.
Runtime environmentWhat gives JavaScript memory, an event loop, and APIs to talk to the outside world.
BrowserFrontend runtime: V8 + browser APIs (DOM, window, fetch).
Node.jsBackend runtime: V8 + Node APIs (file system, networking, processes).
V8 / libuvV8 runs your JavaScript. libuv handles async I/O with the OS and tells Node when work is done.
Event loopKeeps asking "is something ready to run?" and runs the matching callback.
CallbackA function you pass to another function, to be called later when something is ready.
ExpressNode web framework: routing, middleware, request/response handling.
Ports80 = HTTP, 443 = HTTPS (used silently). Any other port must be typed: localhost:3000.

How a request travels

Interview answer

When I type google.com: DNS gives the IP → the browser connects on port 443 → Nginx decrypts HTTPS and forwards plain HTTP to Node on port 3000 → Node runs my code and queries the database → the response goes back the same way, Nginx encrypts it, and the browser shows the page.

The journey of one Google search
BrowserDNSNginxreverse proxyNodeport 3000Database

Press ▶ Play or “Next step”. Arrow keys work too.

Debug view

Step through the diagram to see requests and responses.

DNS records: A and CNAME

Interview answer

DNS turns a name into an IP. An A record points a domain straight to an IP. A CNAME points a name to another name. I point www, api and blog at the main domain with CNAMEs, so if the server IP changes, I update just one A record.

Resolving www.yourapp.com
BrowserDNS resolveryourapp.comDNS records

Press ▶ Play or “Next step”. Arrow keys work too.

Debug view

Step through the diagram to see requests and responses.
RecordMapsExample
Adomain → IPyourapp.com → 65.21.100.200
CNAMEname → another namewww.yourapp.com → yourapp.com

Reverse proxy (Nginx)

Interview answer

Nginx sits in front of my Node app. Users only talk to Nginx on port 443. Nginx decrypts HTTPS, forwards the request as plain HTTP to my app on localhost:3000, and sends the answer back. The same Nginx can spread requests across several app servers (load balancing).

What Nginx actually does
UserNginxport 443 (public)Node applocalhost:3000

Press ▶ Play or “Next step”. Arrow keys work too.

Debug view

Step through the diagram to see requests and responses.

Why not let users hit Node directly?

  • Nginx handles the SSL certificate, so Node never deals with HTTPS.
  • Only ports 80 and 443 are open to the internet. Port 3000 stays private.
  • One Nginx can send traffic to many app servers, and skip one that's down.
nginx
upstream app {
  server 10.0.0.11:3000;
  server 10.0.0.12:3000;
  server 10.0.0.13:3000;
}

server {
  listen 443 ssl;
  server_name yourapp.com;
  ssl_certificate     /etc/letsencrypt/live/yourapp.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/yourapp.com/privkey.pem;

  location / {
    proxy_pass http://app; # plain HTTP to Node
  }
}

SSH vs SSL

Interview answer

SSL (today really TLS) encrypts the traffic between the browser and my server, which turns HTTP into HTTPS; Nginx handles it. SSH is a completely different tool: it lets me log into my server's terminal from my laptop.

SSL / TLSSSH
WhatCertificate that encrypts browser ↔ server trafficRemote terminal access to your server
Who handles itNginx (your Node app never touches it)You, the developer
Commandsudo certbot --nginx -d yourapp.comssh ubuntu@yourserverIP

SSL/TLS is the envelope; HTTPS is HTTP delivered inside that sealed envelope.

Why can't the frontend talk to the database?

Interview answer

Because frontend code is public. To connect, the database password would have to be in the JavaScript, and anyone could read it in DevTools and run any query. The backend keeps the password secret, checks who you are, returns only your data, and runs rules like "check the balance first" the same way for everyone.

Who holds the database password?

Frontend → database

AnyoneFrontend JSPostgresFrontend JS holds—

Frontend → backend → database

Frontend JSBackendPostgresBackend holds—

Press ▶ Play or “Next step”. Arrow keys work too.

Debug view

Frontend → database
Step through the diagram to see requests and responses.
Frontend → backend → database
Step through the diagram to see requests and responses.
  1. Security: the database holds everyone's data. The backend returns only what a given user may see.
  2. Secrets: frontend code is readable by everyone, so the DB password must stay on the server.
  3. Business logic: a withdrawal is check balance → deduct → record transaction → email → update ledger. In the browser, a user could skip the balance check. The backend runs it once, correctly, for everyone.

CORS and the preflight request

Interview answer

CORS is a browser rule: JavaScript on one site can't read a response from another site unless that server allows it with Access-Control-Allow-Origin. The browser enforces it; the backend decides. For requests like DELETE, or ones with an Authorization header, the browser first sends an OPTIONS preflight to ask permission.

CORS: simple request, preflight, and blocked
Frontend JSmyapp.comBrowserenforcesAPIapi.example.com

Press ▶ Play or “Next step”. Arrow keys work too.

Debug view

Step through the diagram to see requests and responses.
  • Who blocks? The browser.
  • Who decides? The backend, with Access-Control-Allow-* headers.
  • Backend → database? No browser involved, so there's no CORS there.

A preflight (OPTIONS) happens for PUT/DELETE, custom headers, an Authorization header, or a Content-Type: application/json body.

js
const cors = require('cors')

app.use(cors()) // development: allow any origin
app.use(cors({ origin: 'https://yourapp.com' })) // production: only your frontend

Core principles: client-server and stateless

Interview answer

Every HTTP request is independent and carries everything the server needs, like the Authorization header. The server doesn't remember earlier requests. That's why any server behind a load balancer can handle any request, and I can scale by just adding servers.

Stateless requests behind a load balancer
BrowserLoad balancerServer 1Server 2Server 3

Press ▶ Play or “Next step”. Arrow keys work too.

Debug view

Step through the diagram to see requests and responses.

If you do keep something in one server's memory, the next request may land on a different server that doesn't have it. The fix is to keep it in shared Redis or the database.

HTTP versions

Interview answer

HTTP/1.0 opened a new TCP connection per request. 1.1 reuses one connection. HTTP/2 sends many requests at once over one connection. HTTP/3 uses QUIC over UDP, so one lost packet only delays its own request, not all of them.

HTTP/1.0 → 1.1 → 2 → 3
BrowserServer

Press ▶ Play or “Next step”. Arrow keys work too.

Debug view

Step through the diagram to see requests and responses.

Anatomy of an HTTP message

Interview answer

Every HTTP message has four parts: the first line (method + URL, or status code), headers, a blank line, and an optional body.

PartWhat it's for
Start lineRequest: method + URL + version. Response: version + status code + text.
HeadersExtra information about the request or response.
Blank lineSeparates headers from the body.
Body (optional)The data: sent in POST/PUT/PATCH, returned in responses. A GET normally has no body.
http
GET /api/users HTTP/1.1
Host: api.shop.com
User-Agent: Mozilla/5.0 (...)
Accept: application/json
Authorization: Bearer abc123token
Connection: keep-alive
http
HTTP/1.1 200 OK
Date: Wed, 27 Aug 2025 10:30:00 GMT
Content-Type: application/json
Content-Length: 187
Cache-Control: no-cache

{ "status": "success", "data": [ ... ] }

Important headers

Interview answer

Headers are extra information about the request or response: the body format (Content-Type), who I am (Authorization), cookies, caching rules, compression, and security rules like HSTS and CSP.

HeaderSent inWhat it doesExample
Content-TypeBothMedia format of the bodyapplication/json
AuthorizationRequestAuth credentialsBearer eyJhbGci…
AcceptRequestResponse format the client wantsapplication/json
CookieRequestCookies stored in the browsersessionId=xyz789
Set-CookieResponseCreate/update a cookiesessionId=xyz789; HttpOnly
Cache-ControlBothHow the response may be cachedmax-age=3600, public
Content-EncodingResponseCompression used on the bodygzip
Content-LengthBothBody size in bytes1024
User-AgentRequestInfo about the clientMozilla/5.0 (…)
Strict-Transport-SecurityResponseAlways use HTTPSmax-age=31536000
Content-Security-PolicyResponseWhich resources may loaddefault-src 'self'
X-Content-Type-OptionsResponseDon't guess the content typenosniff
Referrer-PolicyResponseHow much referrer info is sentstrict-origin-when-cross-origin

Methods and idempotency

Interview answer

Idempotent means sending the same request twice leaves the same final result as sending it once. GET, PUT and DELETE are idempotent; POST is not, so retrying it can create a duplicate order. It matters because networks fail and clients retry.

Idempotency simulator

Send each request, then press “Retry” as if the connection dropped before the response arrived. Watch what the server ends up with.

PUT /users/123idempotent
{ "name": "Ali Khan" }

Requests received by server: 0

Server state

users
id  | name
123 | Ali

Nothing sent yet.

POST /ordersnot idempotent
{ "item": "Laptop" }

Requests received by server: 0

Server state

orders
id  | item
(empty)

Nothing sent yet.

PATCH /accounts/1not idempotent
{ "add": 10 }

Requests received by server: 0

Server state

accounts
id | balance
1  | $100

Nothing sent yet.

The dropped connection

PUT /users/123

AppServerServer holds—

POST /orders

AppServerServer holds—

Press ▶ Play or “Next step”. Arrow keys work too.

Debug view

PUT /users/123
Step through the diagram to see requests and responses.
POST /orders
Step through the diagram to see requests and responses.
MethodIntentIdempotent?
GETRead, change nothingYes
POSTCreate / submitNo
PUTReplace the whole resourceYes
PATCHPartial updateDepends: "add $10" is not; "set price to 900" is
DELETEDeleteYes: already gone is still gone

Status codes

Interview answer

2xx success, 3xx go elsewhere or use your cache, 4xx the client made a mistake, 5xx the server failed. The common ones: 200, 201, 204, 301, 304, 400, 401, 403, 404, 409, 429, 500, 502, 503, 504.

RangeMeaningCodes
1xxInformation100 Continue (keep sending the body)
2xxSuccess200 OK · 201 Created · 204 No Content
3xxRedirect / cache301 Moved permanently · 302 Temporarily elsewhere · 304 Not Modified (use your cache)
4xxClient problem400 Bad request · 401 Not authenticated · 403 Not allowed · 404 Not found · 405 Wrong method · 409 Conflict · 429 Too many requests
5xxServer problem500 Unexpected error · 501 Not implemented · 502 Bad upstream response · 503 Unavailable · 504 Upstream timeout

HTTP caching

Interview answer

The first response includes an ETag, a fingerprint of the content. Next time the browser sends it back in If-None-Match. If nothing changed, the server replies 304 Not Modified with an empty body and the browser uses its saved copy, which saves bandwidth.

ETag and 304 Not Modified
Browser+ cacheServerBrowser holds—

Press ▶ Play or “Next step”. Arrow keys work too.

Debug view

Step through the diagram to see requests and responses.

Express does this for you: res.send() / res.json() add an ETag and answer 304 automatically when If-None-Match matches.

Content negotiation

Interview answer

The client says what it wants with headers: Accept (JSON or XML), Accept-Language, Accept-Encoding (gzip). The server sends the best match from the same URL, or 406 if it can't.

Same URL, different representation

Accept: application/json

ClientServer

Accept: application/xml

ClientServer

Press ▶ Play or “Next step”. Arrow keys work too.

Debug view

Accept: application/json
Step through the diagram to see requests and responses.
Accept: application/xml
Step through the diagram to see requests and responses.

Compression: with Accept-Encoding: gzip, a 26 MB JSON payload can shrink to about 3.8 MB. Almost every production API enables gzip/br.

js
app.get('/report', (req, res) => {
  res.format({
    'application/json': () => res.json({ total: 4820 }),
    'application/xml': () => res.type('xml').send('<report><total>4820</total></report>'),
    default: () => res.status(406).send('Not Acceptable'),
  })
})

Large data transfers

Interview answer

For big uploads I use multipart/form-data, which sends the file as raw bytes in parts. For big downloads I stream the file in chunks instead of loading it into memory, set Content-Disposition, and support Range requests so a broken download resumes with 206 Partial Content.

Uploads, streaming downloads, and resuming
BrowserServer

Press ▶ Play or “Next step”. Arrow keys work too.

Debug view

Step through the diagram to see requests and responses.

Content-Type: text/event-stream is for Server-Sent Events (live updates like notifications or progress), not for file downloads.

js
// download: stream instead of loading the whole file into memory
app.get('/files/:name', (req, res) => {
  const file = path.join(FILES_DIR, path.basename(req.params.name))
  res.attachment(req.params.name) // Content-Disposition: attachment; filename="..."
  fs.createReadStream(file).pipe(res) // chunk by chunk
})
// res.sendFile(file) also handles Range requests (206) for you

TLS and HTTPS

Interview answer

TLS encrypts the traffic so nobody can read or change it on the way, and the certificate proves the server is really who it says it is. The browser checks the certificate against its list of trusted Certificate Authorities. HTTPS is just HTTP inside TLS.

TLS handshake
BrowserServeryourbank.com

Press ▶ Play or “Next step”. Arrow keys work too.

Debug view

Step through the diagram to see requests and responses.

People still say "SSL certificate" out of habit; it's TLS today. Certbot gets and installs a CA-signed certificate for Nginx and renews it automatically:

bash
sudo certbot --nginx -d yourapp.com

Quiz

Think of the answer first, then reveal it.

0/12 answers revealed
  1. Q1 · concept

    Walk through what happens when you type google.com and press Enter.

  2. Q2 · concept

    What's the difference between an A record and a CNAME, and why is a CNAME useful?

  3. Q3 · concept

    Why can't the frontend connect to the database directly?

  4. Q4 · concept

    Who enforces CORS and who controls it? Does CORS protect your API from curl?

  5. Q5 · scenario

    Your SPA's DELETE request fails, and DevTools shows an OPTIONS request with no Access-Control-Allow-Methods. What's happening?

  6. Q6 · concept

    Why does statelessness make HTTP easy to scale?

  7. Q7 · concept

    HTTP/1.1 vs HTTP/2 vs HTTP/3 in one line each?

  8. Q8 · concept

    What does idempotent mean? Which methods are idempotent?

  9. Q9 · scenario

    A payment request times out and the app retries it. The customer is charged twice. Why, and which methods are safe to retry blindly?

  10. Q10 · concept

    Explain ETag and 304 Not Modified.

  11. Q11 · scenario

    You need to let users download 2 GB videos, and resume if the connection drops. How?

  12. Q12 · concept

    What does TLS protect against, and how does the browser trust a server?