Every Express project I started began the same way: I opened the last one and copied four files out of it.
An error handler. Some status constants, because hardcoding 404 in eleven places is how you end up with a 40 somewhere. A few error classes. A wrapper so a failed promise in a route does not quietly hang the request forever.
Copying works fine right up until you fix a bug in the copy and not the original. Then you have four slightly different error handlers in four projects and no idea which one is correct.
So I packaged them. That part took an evening.
The interesting part started after I published it
People used it. Then people opened pull requests.
The first one arrived within days, and it was for the error handler — the thing whose entire job is to be the last line of defence when everything else has failed.
It turned out there was a situation where it would crash. Not handle an error badly. Crash, and take the process with it, in the exact moment you most needed it to work.
I had written that same function four separate times across four projects and never once hit it, because my apps had never done the specific thing that triggers it.
It kept happening
That set the pattern for the next dozen contributions, and every single one was the same shape: a state my own code never produced.
A timeout that fired after the request had already been answered, reporting a failure for something that had succeeded five seconds earlier.
A request logger that silently dropped exactly the requests you most want to see — the ones where the client gave up and hung up mid-way.
Validation that gave up halfway through and left you holding a request that was part-checked and part-not.
A health check you could accidentally break by passing it your own data.
Pagination that cheerfully accepted "10abc" as a number.
None of these are exotic. Every one is something a real server does on an ordinary Tuesday. I just never wrote a test that did it, because it never occurred to me that it happened.
What it gives you
It grew past the original four files, but the rule held: every piece does one thing, and you can read the source of any of it in under a minute.
app.use(requestId());
app.use(requestContext());
app.use(httpLogger({ skip: (req) => req.url === "/health" }));
app.use(errorHandler());The one I reach for most is requestContext, because of what it removes. Anywhere in your code — a service, a helper, six calls deep — you can get a logger that already knows which request you are in:
function findUser(id: string) {
const log = getLogger();
log.info({ userId: id }, "Looking up user");
}No req passed through five function signatures to get there. Every line from that request is tagged with the same id, and findUser never had to know a request existed.
Validation works with whatever you already use. Zod, Yup, or something you wrote yourself — if it can check an object, it fits. I did not want to pick a winner on your behalf.
One peer dependency: Express. Nothing else.
What I would tell past me
The library is not the interesting output here. It is four files I was already copying, plus a build step.
What I actually got was a lesson in the distance between "works in my projects" and "correct". Every bug above was a perfectly legal thing for a server to do that my own apps simply never did. Not edge cases in any meaningful sense. Just other people's normal.
Publishing something small and letting strangers run it against their servers turned out to be a far better test suite than anything I would have written alone — mostly because I would never have thought to write those tests. You cannot test for a state you do not know exists.
The tests exist now. Other people wrote most of them, and I am completely fine with that.
