# Express Fundamentals

## 1. What Express Actually Is

Express is a thin routing and middleware layer on top of Node's built-in `http` module — it does not replace `http.createServer`, it wraps it. Every Express app, at its core, is still a single function passed to `http.createServer(app)` that gets called with `(req, res)` for every incoming request:

```js
const express = require("express");
const app = express();

app.get("/", (req, res) => {
  res.send("Hello, world!");
});

app.listen(3000, () => console.log("listening on :3000"));
```

Express's entire job is to look at `req.method` and `req.url`, decide which of your registered handlers should run, and give you a much nicer `req`/`res` API than the raw Node objects provide (raw `http`'s `res` has no `.json()`, no `.status()` — Express adds all of that as a thin convenience layer on top of the same underlying `ServerResponse` object).

## 2. Routing by Method and Path

```js
app.get("/users", (req, res) => { /* list users */ });
app.post("/users", (req, res) => { /* create a user */ });
app.get("/users/:id", (req, res) => { /* get one user */ });
app.put("/users/:id", (req, res) => { /* replace a user */ });
app.delete("/users/:id", (req, res) => { /* delete a user */ });
```

Express registers each `app.METHOD(path, handler)` call into an internal list, checked **in registration order** against the incoming request's method and path. The first matching route wins — a route registered earlier "shadows" a later route with an overlapping pattern, which is a common source of "my handler never runs" bugs (see Common Pitfalls).

## 3. Route Params vs. Query Params

```js
// GET /users/42?include=orders
app.get("/users/:id", (req, res) => {
  console.log(req.params.id);       // "42"  — from the :id segment of the PATH
  console.log(req.query.include);   // "orders" — from the ?include=... QUERY STRING
  res.json({ id: req.params.id, include: req.query.include });
});
```

`req.params` comes from named segments in the route pattern itself (`:id`) — it only exists because the route was *declared* with that placeholder. `req.query` comes from parsing everything after `?` in the URL, is available on **every** request regardless of route pattern, and every value is always a string (or array of strings for repeated keys) — Express/Node never infers numbers or booleans from the query string.

## 4. `res` Methods and Response Shaping

```js
res.status(201).json({ id: "u1", name: "Ana" }); // sets status THEN sends JSON body
res.send("plain text or HTML");                   // content-type inferred from what you pass
res.sendStatus(404);                               // shortcut: sets status AND sends its text as body
res.redirect("/login");                             // 302 by default
res.set("X-Request-Id", requestId);                 // set a response header
```

`res.status(code)` returns `res` itself (so it chains into `.json()`/`.send()`), and does **not** send the response on its own — nothing goes out over the socket until you call `.send()`, `.json()`, `.end()`, or similar. Forgetting to call one of those terminal methods leaves the client hanging with no response until it times out.

## 5. Parsing Request Bodies

```js
app.use(express.json());              // parses application/json bodies into req.body
app.use(express.urlencoded({ extended: true })); // parses HTML form submissions

app.post("/users", (req, res) => {
  console.log(req.body); // { name: "Ana", email: "ana@example.com" } — only populated
                          // because express.json() ran first and parsed the raw stream
});
```

Without registering `express.json()` first, `req.body` is `undefined` even for a request that clearly has a JSON body — Express does **not** parse request bodies by default, because reading and buffering the body is itself a design choice (streaming large uploads shouldn't be forced through a JSON parser). This is the first concrete example of middleware ordering mattering, expanded on next.

## 6. Global Middleware with `app.use()`

```js
app.use((req, res, next) => {
  console.log(`${req.method} ${req.url}`); // runs for EVERY request, before any route handler
  next(); // MUST call this, or the request hangs forever — nothing downstream ever runs
});

app.get("/", (req, res) => res.send("home"));
```

`app.use()` without a path registers a handler that runs for every request, in the order it was registered, **before** the matching route handler — this is Express's middleware chain, covered in depth in the dedicated `06-middleware/` topic in this group. The critical rule introduced here: every middleware function must call `next()` (to hand off to the next one in the chain) or send a response itself — doing neither leaves the request permanently unresolved.

![Express keeps an ordered stack of layers, body parser, cors, routes, 404 and error handlers, tried top to bottom; handlers read params, query and body and answer through res methods](diagrams/01-layer-stack.svg)

## 7. Minimal and Unopinionated by Design

Express deliberately does not prescribe a folder structure, a dependency-injection system, a validation library, or an ORM — it hands you `app.get/post/...`, `app.use()`, and a nicer `req`/`res`, and nothing else. This is the direct contrast with NestJS (covered in `02-nestjs_fundamentals/`), which is built **on top of** Express (or Fastify) and adds an opinionated, decorator-based structure — modules, controllers, dependency injection — as a deliberate answer to the fact that large Express codebases tend to reinvent that structure inconsistently, project by project, once they outgrow a handful of route files.

## Common Pitfalls

1. **Registering a more general route before a more specific one that should take precedence.** `app.get("/users/:id", ...)` registered before `app.get("/users/me", ...)` means `/users/me` gets swallowed by the `:id` pattern (`"me"` matches `:id` just fine) and the more specific handler never runs — order matters, most-specific-first.
2. **Forgetting `express.json()` before reading `req.body`.** `req.body` is silently `undefined`, not an error — easy to miss until a handler mysteriously "loses" data that was clearly sent.
3. **Calling `res.json(...)` more than once for the same request.** Node throws `ERR_HTTP_HEADERS_SENT` — a common cause is an `if` branch that sends a response but forgets to `return`, so code after it also tries to send.
4. **Forgetting to call `next()` in a middleware that doesn't itself send a response.** The request hangs until the client times out — no error is thrown, which makes it easy to miss in testing.
5. **Doing real business logic directly inside route handlers.** Works fine for a tiny app, but doesn't scale — this is exactly the thin-controller principle covered in `05-controllers/`.

## Quick Reference

```
app.get/post/put/delete(path, handler)  -- register a route
req.params.<name>   -- from :name in the route PATH (only exists if declared)
req.query.<name>     -- from ?name=... in the URL, always present, always a string
res.status(code).json(obj)  -- chainable; nothing sent until a terminal method is called
app.use(fn)           -- global middleware, runs in registration order, before route handlers
express.json()         -- MUST be app.use()'d before req.body is populated for JSON requests
next()                  -- hands off to the next middleware/handler; omitting it hangs the request
```
