# Week 2 Study Plan: Routers, Middleware, and Supertest

This guide explains the Week 2 class and the finished books assignment. Read
the files in this order: `types.ts`, `books-service.ts`, `books-router.ts`,
`app.ts`, `index.ts`, and finally `__tests__/books.test.ts`.

## What you should be able to do

By the end of the week, you should be able to:

1. use TypeScript types in an Express project;
2. split routes into an Express router;
3. separate data operations from HTTP handlers;
4. write middleware and explain why its registration order matters;
5. use headers for simple token checks;
6. choose appropriate HTTP status codes;
7. split the Express app from the listening server;
8. test requests and responses with Vitest and Supertest.

## 1. Understand the project layers

The application separates five responsibilities:

```text
index.ts -> app.ts -> books-router.ts -> books-service.ts -> catalog array
                         |
                         -> types.ts describes the data
```

- `types.ts` defines what a book looks like.
- `books-service.ts` owns the in-memory catalog and its operations.
- `books-router.ts` translates HTTP requests into service calls.
- `app.ts` assembles the Express application and final error handler.
- `index.ts` opens port 6790 when the app runs as a real server.

This separation makes the code easier to understand and lets tests import the
app without starting a fixed network listener.

Checkpoint: explain why a route should call `getBook(id)` instead of reading
the `catalog` array directly.

## 2. Review the TypeScript pieces

The `Book` interface requires an ID, title, author, year, and status.
`BookStatus` limits status to the supplied enum values. The service function
signatures show what every operation accepts and returns.

Project-local imports end in `.js`, even though the source files end in `.ts`:

```ts
import { getAllBooks } from './books-service.js';
```

TypeScript compiles the files to JavaScript, and Node ultimately loads the
`.js` files. The `NodeNext` configuration checks imports using those Node ES
module rules.

`updateBook()` accepts `Partial<Omit<Book, 'id'>>`:

- `Omit<Book, 'id'>` means all book fields except the ID;
- `Partial<...>` makes those remaining fields optional.

That type fits PATCH: a request may change only one field, but it should not
replace the book's identity.

## 3. Learn the service layer

`books-service.ts` keeps the catalog private and exports focused operations:

| Function | Job |
| --- | --- |
| `addBook` | add one book |
| `getAllBooks` | return the catalog |
| `getBook` | find a book by numeric ID |
| `updateBook` | apply selected changes to one book |
| `removeBook` | remove the matching ID |
| `_resetCatalog` | empty test state before each test |

The catalog uses `let` because removing or resetting books replaces the array.
`removeBook()` uses `filter()` to keep every book whose ID does not match.

The leading underscore in `_resetCatalog()` signals that application routes
should not call it. It exists so tests remain independent.

Exercise: start with three book IDs on paper and walk through `removeBook()`
for the middle ID. Write the array that `filter()` returns.

## 4. Follow middleware in order

Middleware is a function that receives `req`, `res`, and `next`. It either:

- calls `next()` so Express continues; or
- sends a response and stops the chain.

The router-wide order is:

```text
express.json() -> logger -> matching route
```

The POST route adds two route-specific steps:

```text
JSON parser -> logger -> checkAuth -> validateBookParams -> POST handler
```

Auth runs before validation. A request with no valid token and a bad book body
returns 403 because it stops at `checkAuth`; validation never runs.

`checkAuth` reads the `Authorization` request header. The expected value is:

```text
Authorization: Bearer SI679
```

This shared string is only a classroom example. A real application would not
hard-code a credential in source code.

`validateBookParams` requires title and author to be strings containing at
least one non-space character. It returns 400 with a helpful message when the
submitted book fails that check.

Checkpoint: what happens if middleware neither sends a response nor calls
`next()`? The request stays open and appears to hang.

## 5. Walk through the API

| Request | Auth? | Result |
| --- | --- | --- |
| `POST /books` | yes | validate, create a book, return 201 and JSON |
| `GET /books` | no | return all books or exact-match filters |
| `GET /books/:id` | no | return one book or 404 |
| `PATCH /books` | yes | update selected fields, 400 for bad input, 404 if absent |
| `DELETE /books` | yes | remove the ID and return 200 |
| `GET /books/badroute` | no | throw an example error handled as 500 |

### Query filtering

GET `/books` checks `title`, `author`, and `year`. Every supplied filter must
match, so author and year together act like an AND condition. Query values are
strings; the code converts `year` to a number before comparing it with a
book's numeric year.

### GET by ID

Route parameters are strings. `Number(req.params.id)` converts the route value
before the service compares it with a numeric book ID.

### POST status

The assignment changes a successful POST from the default 200 to 201 Created.
The response contains the created book, including its generated ID and default
available status.

### PATCH validation

The body must contain an `id` plus at least one field to change. The router
separates them with:

```ts
const { id, ...changes } = req.body;
```

`changes` contains everything except `id`. The service applies those fields to
the existing book with `Object.assign()`.

## 6. Understand the app/server split and error handler

`app.ts` creates and exports the Express app. `index.ts` imports it and calls
`listen()`. Supertest imports `app.ts`, so it can make requests without asking
you to start the server first.

The final error handler has four parameters:

```ts
(err, req, res, next)
```

Express uses that four-parameter shape to recognize error middleware. It is
registered after the router because Express looks forward through the chain
when a handler throws.

Checkpoint: move the error handler above the router in a drawing. Why would an
error thrown later fail to reach it?

## 7. Read the tests as examples of API use

Supertest builds a request with a readable chain:

```ts
await request(app)
  .post('/books')
  .set('Authorization', 'Bearer SI679')
  .send({ title: 'The Hobbit', author: 'J.R.R. Tolkien', year: 1937 });
```

The test then checks `res.status`, `res.body` for JSON, or `res.text` for a
plain text response. The Week 2 tests cover:

- empty initial state;
- exact author filtering;
- missing and incorrect tokens;
- blank-title validation and its message;
- POST creation and 201;
- GET by ID and missing-ID 404;
- PATCH auth, input validation, partial update, and unchanged fields;
- DELETE auth and final empty state.

`beforeEach()` calls `_resetCatalog()`. Without it, a book posted by one test
would remain for the next test and make the outcome depend on test order.

## 8. Suggested study session

### First 20 minutes: draw the architecture

- Draw all five source-file layers.
- Follow one POST request from `app.ts` through middleware and the service.
- Follow the response back to the client.

### Next 25 minutes: middleware practice

- Write the ordered chain for GET, POST, PATCH, and DELETE.
- Predict which status wins for a request with both bad auth and bad data.
- Use Postman to compare no token, the wrong token, and the correct token.

### Next 25 minutes: route behavior

- POST two books by different authors.
- Filter by one author, then by author plus year.
- GET one returned ID.
- PATCH its title and verify its author did not change.
- DELETE it and confirm it is absent.

### Final 20 minutes: tests

- Run `npm test`.
- Read one test at a time using arrange, act, assert:
  set up data, send the request, check the result.
- Temporarily remove `_resetCatalog()` and observe how shared state changes the
  suite. Restore it before finishing.

## Final self-check

You are ready for Week 3 when you can answer these without opening the code:

1. Why use a router instead of putting every route in `index.ts`?
2. What are the two valid ways for middleware to finish its work?
3. Why does middleware order change the response?
4. What do 201, 400, 403, 404, and 500 mean here?
5. Why are `app.ts` and `index.ts` separate?
6. Why does every test reset the catalog?
7. When should a test inspect `res.body` versus `res.text`?

Useful commands:

```bash
npm run typecheck
npm test
npm run build
npm run dev
```
