Synthetic Industry

Inspectable example · updated 2026-10-11

Synthetic Express 4 to 5 URL table: patterns, async errors, dotfiles and a removed method

An invented before-and-after table shows how to check that converted route patterns still match the right URLs, with each expected Express 5 result taken from the migration guide.

An example, not a customer case study. Scope and evidence limitations are described below.

What this example is

This is a synthetic table for an invented service. The routes are made up, and no application was upgraded for this example. The Express 4 forms in the left column are the forms the migration guide says to change, and every Express 5 expectation in the table comes from that guide's description of the new behaviour. A real job runs each row against a copy of the application and records what actually happens.

  • Each row has one URL that must succeed and, where a pattern changed, a URL that must not match.
  • The expected results are specifications to check, not observations from a run.

Patterns

The first three rows are patterns that changed meaning. A bare wildcard must be named, and its value arrives as an array. An optional extension uses braces. Unmatched parameters are omitted, not an empty string.

If the matrix is wider than the box, scroll horizontally to read every column. Keyboard: focus the matrix and use Left/Right.

route (Express 4)     | route (Express 5)      | request             | expected on Express 5
/files/*              | /files/*splat          | GET /files/a/b.txt  | 200; params.splat is ['a','b.txt']
/:file.:ext?          | /:file{.:ext}          | GET /report         | 200; file 'report', ext omitted
/:file.:ext?          | /:file{.:ext}          | GET /report.csv     | 200; file 'report', ext 'csv'

Methods, bodies and errors

The next rows cover a removed argument form, a missing body and an async failure. After conversion, a status is set with res.status before json. A request with no body parser run leaves req.body undefined, so code that read a property of it must guard. An async handler that throws reaches the application's error handler.

If the matrix is wider than the box, scroll horizontally to read every column. Keyboard: focus the matrix and use Left/Right.

case                 | Express 4 form              | Express 5 form                   | expected on Express 5
create returns 201   | res.json(obj, 201)          | res.status(201).json(obj)        | 201 with the JSON body
POST with no body    | req.body was {}             | req.body is undefined            | handler must guard; no crash
async handler throws | not covered by this table   | error forwarded to error handler | error handler's response (500 page)
redirect back        | res.redirect('back')        | res.redirect(req.get('Referrer') || '/') | 302 to referrer or '/'

Static files and hidden directories

Static serving ignores dotfiles by default in Express 5, including hidden directories, so a well-known path that was served before may now be a 404 unless the option is set. The check is explicit because it is easy to miss.

If the matrix is wider than the box, scroll horizontally to read every column. Keyboard: focus the matrix and use Left/Right.

request                          | expected on Express 5 by default | with dotfiles allowed
GET /.well-known/security.txt    | 404                              | 200
GET /public/logo.png             | 200                              | 200

What this does not prove

A table like this shows that the agreed URLs behave as specified. It does not show that every request your users and integrations send behaves the same, or that the app is faster, safer or free of other defects. The fixed job lists every changed pattern, runs the agreed table before and after and records each accepted difference.

  • Ask what URLs a proposal will test and who chose them.
  • Prices on the linked job are untested proposals; payment follows agreed checks.

Sources and limits

  • Express: migrating to Express 5 Checked 2026-10-11.
    • Wildcards must be named, such as /*splat, and wildcard parameters are arrays; optional parameters use braces, as in /:file{.:ext}; unmatched parameters are omitted.
    • res.json(obj, status) is replaced by res.status(status).json(obj); res.redirect('back') is replaced by redirecting to the Referrer header or a fallback.
    • Async handlers that throw or reject forward the error to the error handler; req.body is undefined when no parser ran; express.static ignores dotfiles by default so /.well-known paths return 404 unless dotfiles are allowed.