Test and ship Express

Finish the notes application

Complete the HTML and JSON paths, security controls, tests, observability, and recovery notes as one coherent service.

An Express app is finished when you can explain what happens to any request, in success and in failure, without opening the code. The notes app has every piece now. Let’s close the loop.

The request path, end to end

Read createApp() top to bottom and you should see this order:

export function createApp({ config, notes, sessionStore }) {
  const app = express()
  app.set('trust proxy', config.trustProxy)

  app.use(helmet())
  app.use(requestId)
  app.use(logger)
  app.use('/assets', express.static(publicDir, { maxAge: '1h' }))
  app.use(session({ /* from config */ }))

  app.use('/api', apiRouter({ notes }))
  app.use('/', pagesRouter({ notes }))

  app.use((req, res) => res.status(404).send('Not found'))
  app.use(errorHandler)

  return app
}

Headers, id, log, static, session, API, pages, not found, errors. If you can recite that, you can debug the app.

Run the failure matrix

Before shipping, I run the whole matrix against the staging copy:

ConditionExpected
valid form POST /notes303 to the new note
valid POST /api/notes201, Location, JSON body
empty title, both paths400 with the message
20 KB body413
text/plain to the API415
someone else’s note403
unknown id404
database stopped500, generic body, full log line
SIGTERM during a slow requestrequest completes, then exit 0
restartstill logged in
forged X-Forwarded-Forignored
Origin: https://evil.exampleno CORS headers

Save the curl output next to the table, so when a change breaks a row you have the before and after. Shutdown and restart aren’t automated, so I rerun those by hand on every release.

Write the operating contract

The README is for whoever runs this at 3 AM, and that may be you. Six short sections:

  1. Setup. Node version, npm ci, each env variable.
  2. Routes. HTML paths and redirects, API paths and status codes.
  3. Data. Where notes and uploads live, what a restart or a volume loss destroys.
  4. Sessions. Cookie name, lifetime, the store.
  5. Tests. npm test and the manual matrix above.
  6. Limits. Body and upload sizes, the shutdown timer, what the proxy must match.

If a section needs a paragraph, something in the app is too clever.

Hand it to someone else

Hand the repository to a colleague with only the README. They start it, run the tests, create a note, upload an image, stop it with SIGTERM, read the log. Every question they ask is a missing README line.

When they can do all of that without you, the notes app is done. Not perfect, done: a small Express 5 service whose request path you can draw, whose failures you can name, and whose behavior another person can verify. Try the twelve rows on your own app too. The ones that make you hesitate point at the lessons worth reading twice.

Lesson completed