I moved the 2026 Node Express template from a quick prototype to a production-grade infrastructure. I spent the last few weeks removing fragile code and replacing it with patterns that hold up under stress. I do not have time for code that might work or might crash based on how a third-party library feels that day. I needed a fortress.
Unifying Error Handling
The biggest win was unifying the error handling. Most Node.js apps are a mess of random try/catch blocks and bubbling exceptions. I moved to a Result pattern across the User and Authentication layers.
- Logic as Data: Every service and repository returns a
{ success: true, data }or{ success: false, error }object. - Predictable Flow: This creates a railroad where the controller just checks a boolean and either ships the data or hands the error to the global handler.
- No Explosions: I stopped using
throwfor expected domain logic like missing users or invalid passwords.
Forcing the backend to treat errors as data makes the entire system more stable. It is the only way to build a reliable system without the logic collapsing under its own weight.
Hardening the AI Stream
The template includes assistant features, so it needs to stream data fast. I had a lot of proxy slop in the router that I had to clean up.
- Infrastructure vs Logic: I pulled the streaming plumbing out of the router and into a dedicated AI Controller and Service.
- Resource Management: I extended the Express Request type to include a proper
AbortSignal. - Kill Signals: Now, if a user closes their browser, the signal propagates to the internal API and kills the generation immediately.
This prevents me from burning compute cycles on data that no one is reading.
Plugging the Security Holes
The most critical part of this refactor was addressing the data leak identified during the codebase analysis. Endpoints like GET /api/users were returning hashed passwords because the repository and controller were not separated by a security layer.
- The DTO Gatekeeper: I implemented a
toSafeUsermapper in the User Service. - Security by Default: The service layer now acts as a boundary. It is impossible for a password hash to reach the controller because the return type is strictly a
SafeUser. - Repository Purity: The
UserRepositorystill handles the raw data for internal checks, but it never leaks to the public API.
Closing the Loop
I am officially in the endgame for this foundation. The railroad is complete, the types are airtight, and the linter is finally quiet. I have reached a professional standard that allows me to stop worrying about the how and start focusing on the what.
