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:
| Condition | Expected |
|---|---|
valid form POST /notes | 303 to the new note |
valid POST /api/notes | 201, Location, JSON body |
| empty title, both paths | 400 with the message |
| 20 KB body | 413 |
text/plain to the API | 415 |
| someone else’s note | 403 |
| unknown id | 404 |
| database stopped | 500, generic body, full log line |
SIGTERM during a slow request | request completes, then exit 0 |
| restart | still logged in |
forged X-Forwarded-For | ignored |
Origin: https://evil.example | no 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:
- Setup. Node version,
npm ci, each env variable. - Routes. HTML paths and redirects, API paths and status codes.
- Data. Where notes and uploads live, what a restart or a volume loss destroys.
- Sessions. Cookie name, lifetime, the store.
- Tests.
npm testand the manual matrix above. - 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