Full Stack
Beginner 12 min read Full Stack

Full Stack Architecture

A full stack app is the client that renders the UI, the server that runs business logic and serves APIs, and the data store that persists state. Learn the request lifecycle, the three-tier model, and where rendering happens (CSR, SSR, SSG) so you can reason about any framework.

three-tierclient-serverAPICSRSSRSSGdatabaserequest lifecycle
Introduction

What is this?

Full stack architecture is how a client, a server and a database cooperate to deliver a web app. The client (browser or mobile app) renders UI and talks to the server over HTTP; the server validates requests, runs business logic and reads or writes data; the database stores the durable state. The frontend and backend are coupled only by the API contract — the endpoints, payloads and status codes they agree on — so each layer can be built, tested and scaled independently.

Motivation

Why do you need it?

  • Separation of concerns: UI, business logic and persistence live in separate layers that stay testable and replaceable.
  • Scaling independently: the API can run on many servers while the database runs on its own cluster; the client is pure static assets.
  • A stable API contract lets frontend and backend teams ship in parallel without waiting on each other.
  • Security boundaries: validation and authorization belong on the server, where clients can be trusted least.
  • Knowing where rendering happens explains real tradeoffs: SEO, time-to-interactive, bundle size and data freshness.
Think about it

Mental model

Imagine

A restaurant

Think of

A full stack app

The dining room is the frontend, the kitchen is the backend, the walk-in fridge is the database, and the waiter takes orders in a fixed format (the API contract). Guests never touch the fridge — they only speak to the waiter.

Mechanics

How it works

  • The client loads the app, then issues an HTTP request (GET, POST, PUT, DELETE) to a server endpoint such as GET /api/items.
  • The server routes the request, authenticates the caller, and runs validation and business logic.
  • The server queries the database through an ORM or query builder, mapping rows to objects.
  • The server responds with a status code and a JSON (or HTML) payload; the client parses it and re-renders.
  • Where rendering happens decides the shape of the response: CSR ships a JS bundle that renders in the browser, SSR renders HTML on the server per request, and SSG renders HTML at build time.

The request lifecycle across the three tiers.

Browser / client
HTTP request
API server
Database
JSON response
Client re-renders
Reference

Key commands

npm create vite@latest my-app

Scaffold a new frontend app with Vite.

Scaffolding project in my-app/...\nDone. Now run: npm install
npm install express

Add the Express server framework to the project.

node server.js

Start the backend so it listens for HTTP requests.

API on :3001
curl http://localhost:3001/api/items

Hit the endpoint directly from the terminal to inspect the JSON contract.

[{"id":1,"name":"First item"}]
npm run dev

Start the frontend dev server with hot reload.

npx tsc --noEmit

Type-check the whole project without emitting files.

Hands-on

Examples

A minimal client and server exchanging JSON

A minimal client and server exchanging JSON
// server.js
const express = require("express")
const app = express()
app.get("/api/items", (req, res) => {
res.json([{ id: 1, name: "First item" }])
})
app.listen(3001, () => console.log("API on :3001"))
// client (browser)
const items = await fetch("/api/items").then((r) => r.json())
console.log(items[0].name) // "First item"

The server exposes one JSON endpoint. The client calls fetch, receives the payload, and works with plain data. The only coupling is the URL and the JSON shape — the server could be rewritten in another language without touching the client as long as the contract holds.

Real world

In production

  • Next.js and Remix unify the stack: server code, client components and routes live in one codebase while still hitting external APIs and databases.
  • Microservice backends expose a BFF (backend-for-frontend) that aggregates several internal APIs into the exact shape one client needs.
  • Headless commerce: a React storefront consumes a commerce API, so checkout and inventory logic stay server-side and PCI-relevant.
  • Static marketing sites (SSG) deploy to a CDN for near-zero latency, while app routes that need live data are server-rendered or client-fetched.
Guidelines

Best practices

  • Define the API contract first (types or an OpenAPI spec) so both sides code against the same shapes.
  • Keep business logic on the server — never trust a client to enforce rules or validate input.
  • Treat the frontend and backend as separate deployables with their own tests and versioning.
  • Use environment variables for anything that differs between local, staging and production.
  • Document status codes and error shapes so the client can render failures predictably.
Watch out

Common mistakes

  • Duplicating business rules in the client 'for UX' and diverging from server validation.
  • Fetching data from the browser directly against the database port, bypassing the API entirely.
  • Hard-coding API base URLs, which breaks the moment staging and production differ.
  • Shipping sensitive logic (payment amounts, authorization) to the client bundle where users can read it.
Root cause analysis

Common issues

Error
Failed to fetch / net::ERR_CONNECTION_REFUSED
Root cause

The client and server are not on the same port/host, or the server is not running.

Fix

Start the server, and make sure the client points at the server URL (often http://localhost:3001).

Error
429 Too Many Requests
Root cause

The client is hammering the API (often an infinite render loop calling fetch).

Fix

Move the fetch into an effect with stable dependencies, or add client-side caching.

Error
CORS error: no 'Access-Control-Allow-Origin' header
Root cause

The browser blocks a cross-origin request the server never allowed.

Fix

Configure CORS on the server to allowlist the exact client origin (do not use * with credentials).

When things break

Debugging tips

  • Inspect the Network tab in DevTools first: status code, response body and timing reveal which tier failed.
  • Replay any request with curl or Postman to isolate whether the bug is in the server or the client.
  • Add request IDs to logs so you can trace one request across client, server and database.
  • Run the backend in isolation (curl the endpoint) before involving the frontend.
Interviews

Interview checkpoint

Practice

Hands-on lab

Wire Up a Three-Tier App
40 min · beginner
In progress
Objectives
  • Scaffold a frontend app with Vite
  • Stand up an Express API that returns JSON
  • Fetch the data from the browser and render it
  • Trace the request through the network tab

Wire Up a Three-Tier App

40 min · beginner

0/5 steps
0%
sandbox · Linux sandbox
$
Try these commands
Test yourself

Quick quiz

0/3
1

In a three-tier app, which component talks directly to the database?

2

A server-rendered page returns finished HTML to the browser instead of an empty shell.

3

Which layer should enforce the business rule that an order total must be positive?

Quick reference

Cheat sheet

Request lifecycle
GET /api/itemsClient requests a resource
status 200/201Success with payload
status 4xxClient error (400 bad input, 401/403 auth)
status 5xxServer/database failure
Content-Type: application/jsonPayload format in responses
Rendering strategies
CSRJS renders in the browser — most dynamic
SSRHTML rendered per request — SEO + fresh
SSGHTML built once — fastest, static
Go deeper

Official documentation