# Capstone: The Complete Canteen API

Course: [Express.js](https://www.devtutorials.ai/learn/expressjs) · Section: Architecture and Reference
Source: https://www.devtutorials.ai/learn/expressjs/architecture-reference/capstone-canteen-api

> One full project: SQLite tables, hashed passwords, JWT login, staff and student roles, validated orders with ownership checks, central errors, supertest tests, OpenAPI docs, health checks and graceful shutdown.

## Capstone: The Complete Canteen API

Time to put the whole course together. You will build the real Green Valley School Canteen API, step by step.

- **Capstone** = one final project that uses everything you have learned.
- It has a **database**, **login with tokens**, **roles**, **validation**, **error handling**, **tests** and **API docs**.
- Each step adds a few files. The final project is the same one you would deploy.
- Every lab runs the full project, so you can read and change any file.

**Simple lesson:** Real APIs are many small parts. Each part does one job.

### What You Will Build

| Who | Can do |
|---|---|
| Anyone | Read the menu |
| Students (Priya, Arjun) | Log in, place orders, read **their own** orders |
| Staff (Meera) | Add menu items, read **every** order |

The project layout:

```text
canteen-api/
  package.json
  src/
    config.js        settings from the environment
    db.js            SQLite tables and sample data
    errors.js        HttpError, 404 and the error handler
    auth.js          signToken, requireAuth, requireRole
    validate.js      checks for orders and menu items
    openapi.js       API description
    routes/
      menu.js  auth.js  orders.js
    app.js           builds the app (used by tests)
    server.js        starts it and handles SIGTERM
  test/
    api.test.js
```

In this project, you will:

1. Store the menu in SQLite and answer clear errors.
2. Log in with hashed passwords and JWTs.
3. Place orders with validation and ownership checks.
4. Test the whole API with `node --test` and supertest.
5. Add docs, a health check and graceful shutdown.

## Step 1: Database, Menu and Errors

- `db.js` creates three tables: `users`, `menu`, `orders`. Prices are whole rupees.
- `errors.js` has `HttpError`. Any route can `throw new HttpError(404, "...")`.
- Express 5 catches thrown errors, even in async code, and sends them to `errorHandler`.
- The error handler hides details of 500 errors, but shows clear messages for 4xx errors.
- See **Working with a Database** and **Error Handling**.

- Adding a menu item without a token gets 401. You will log in next.

**Simple lesson:** Throw an `HttpError` anywhere. One error handler answers for all of them.

## Step 2: Logging In

- Passwords are stored as a **salt** and a **PBKDF2 hash**, never as plain text.
- Login hashes the given password with the same salt and compares with `timingSafeEqual`.
- A correct login returns a 15-minute JWT with `sub`, `role` and `name`.
- `requireAuth` checks the token. `requireRole("staff")` checks the role.
- See **Cookies and Sessions** and **Token Authentication and Authorization**.

- The same message for "no such user" and "wrong password" stops attackers from guessing usernames.
- Try logging in as `priya` with `idli123`, then use her token on `POST /api/menu`.

**Simple lesson:** Hash passwords. Sign a token at login. Check the token and role on protected routes.

## Step 3: Orders

- The whole orders router uses `requireAuth`, so every order route needs a login.
- `validateOrder` rejects bad input with **400** and a `details` object per field.
- The user ID comes from the **token** (`req.user.sub`), never from the request body.
- Students see only their own orders. Staff see all. Other people's orders answer **404**.
- A JOIN adds the item name and price, so the total is worked out by the server.

**Simple lesson:** Validate, take the user from the token, and check ownership on every order route.

## Step 4: Testing Everything

- `app.js` builds the app **without** calling `listen`. Tests import it directly.
- supertest sends requests to the app in memory. No port needed.
- One test per rule: public menu, login needed, own orders, other orders hidden, bad input.
- Run `npm test` before every commit and in CI. See **Application Structure**.

When you add a feature, add a test for it first. Then change the code until it passes. This habit is called **test-driven development**.

**Simple lesson:** If it is not tested, assume it is broken.

## Step 5: Docs, Health and Shutdown

- `GET /api/openapi.json` describes every route, so other teams know how to use your API. See **Building REST APIs**.
- `GET /healthz` lets the host check the app is alive.
- Broken JSON gets a clean **400**, not a crash.
- On SIGTERM, the server finishes current requests, closes the database, then exits.
- See **Deploying an Express App**.

To run this on your computer, create the folders, copy each file, run `npm install`, then `npm run dev`. Node.js 22.5 or later is needed for `node:sqlite`.

**Simple lesson:** Document it, check its health, and stop it cleanly. Then it is ready to ship.

## Where to Go Next

Your API is complete. Here are good next steps:

- **A real database file or PostgreSQL**, with migrations. See **Working with a Database**.
- **Refresh tokens** in httpOnly cookies, so students stay logged in.
- **Rate limits** on `/api/auth/login`. See **Express Security**.
- **Request logs** with request IDs. See **Logging and Debugging**.
- **Live order updates** for the kitchen screen with SSE. See **Calling APIs, Background Jobs and Real-Time**.
- **TypeScript** for the whole project. See **Express with TypeScript**.
- **A front end**: a React or Angular app that calls this API.

Before going live, set a long random `JWT_SECRET` on the host. The default in `config.js` is only for learning.

## Common mistakes

<div className="tscroll">

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| `app.listen` inside `app.js` | One file is simpler | Build in `app.js`, listen in `server.js` | Tests try to open a port and clash |
| Taking `userId` from the body | The client knows who it is | Use `req.user.sub` from the token | Students place orders as other students |
| Different login errors for user and password | Clear messages help | One message for both | Attackers learn valid usernames |
| Total price sent by the client | Saves a query | Work it out on the server | Students pay 1 rupee for a dosa |
| Each route sends its own errors | It is more flexible | Throw `HttpError`, one handler answers | Messages and status codes do not match |
| No tests for "not allowed" cases | Happy paths are enough | Test 401, 403 and 404 paths too | Security bugs reach production |

</div>

## Check Yourself

## Exercises

## What to Remember

- Split the project into small files: config, database, errors, auth, validation, routes, app, server.
- Throw `HttpError` from anywhere. One error handler answers every error.
- Hash passwords, sign JWTs at login, and check tokens and roles with middleware.
- Validate every input, take the user from the token, and check ownership.
- Test with supertest on `app.js`, including the 401, 403 and 404 cases.
- Add docs, a health check and SIGTERM handling, then deploy.

Next: **Express.js Cheat Sheet**. Every important Express 5 API on one page.
