Node.js Backend — What the Framework Hides
A Router Is One Table
In one line
A framework's router is not magic; it is a single table. It uses the method and path as the key and the handler as the value, and looks entries up. What that table distinguishes determines the character of the API.
Why it is needed: 404 and 405 are different events
Say you send DELETE to /items. The path exists, but it does not accept that method.
If you return 404 here, the client reads it as "there is no such resource" and starts doubting the path.
If you return 405, the client reads it as "the resource exists, but this action is not allowed." This is
where debugging time is won or lost.
With a framework, this distinction comes for free. So if you never build it yourself, you pass by without even knowing the distinction exists.
How to lay out the table
const routes = [
{ method: 'GET', path: /^\/healthz$/, handler: health },
{ method: 'GET', path: /^\/items$/, handler: listItems },
{ method: 'POST', path: /^\/items$/, handler: createItem },
{ method: 'GET', path: /^\/items\/(\d+)$/, handler: getItem },
];
The lookup happens in two parts. First collect every entry whose path matches, then pick the one
whose method matches among them. If no path matches, return 404; if a path matches but no entry has the
method, return 405. If you look up method + path in a single step, this distinction disappears.
In the field
Whether it is Nest or Express, they only fill this table with decorators or app.get(), and the structure is
the same. So where to look when routing does not work is also the same: the registration order and the scope of
the pattern. If you put a wide pattern (/*) at the top, the rules below it are never reached.
It is exactly the same trap you saw in the Envoy lab, and it is truly the most common report.
When you read a framework for the first time, start with the router, because it is the way that framework divides up the world.