Cloud & Databases

AI Express.js Middleware: 13 Rules for Claude.md

Your AI assistant is writing Express.js code. Great. Until it isn't. A new convention, CLAUDE.md, aims to bridge the gap between human intent and AI execution, one middleware rule at a time.

A flowchart showing the flow of Express.js middleware, highlighting potential error points and CLAUDE.md rule enforcement.

Key Takeaways

  • AI code generation for Express.js often fails due to missed version-specific conventions, particularly regarding async error handling in Express 4.
  • CLAUDE.md acts as a detailed specification file for AI, dictating rules for middleware signatures, async error handling, and post-`next()` operations to prevent common bugs.
  • Enforcing strict input validation with libraries like `zod` and proper router scoping for middleware are key rules to prevent fragile code and security vulnerabilities.
  • The CLAUDE.md approach represents a shift towards prompt engineering as an architectural control plane, embedding governance into AI workflows for more predictable development.

Is your AI code generator a secret saboteur of your Express.js applications?

We’ve all been there. You prompt Claude to spin up a new route, or perhaps refactor a gnarly piece of middleware, and it churns out code that looks plausible. It passes the initial sniff test. But then, you run it. And suddenly, the headers are already sent, the error handler is sniffing for phantom arguments, or—most insidiously—async route rejections vanish into the digital ether like they never happened. These aren’t the result of some grand AI conspiracy; they’re usually the consequence of the AI not knowing the finer, almost arcane, conventions of your specific Express.js stack.

Enter CLAUDE.md.

This isn’t another breathless announcement about AI synergy. It’s a pragmatic, engineer-driven attempt to inject sanity into the AI-assisted coding process for one of Node.js’s foundational web frameworks. The core idea is simple: treat your AI like a junior developer who needs a super-detailed spec. A CLAUDE.md file acts as that spec, a contract outlining the non-negotiable rules your AI must adhere to when generating Express.js code.

Why is this necessary? Because Express, particularly version 4, operates on a series of implicit agreements. Middleware signature matters. The order of operations is paramount. And crucially, async error handling, while much improved in newer Node.js versions and Express 5, still requires explicit guidance in older, widely deployed codebases. Claude, in its quest for generality, often misses these nuanced, version-specific requirements. It doesn’t inherently understand your project’s package.json, let alone the subtle dance your middleware performs.

The Version Lock: A Foundation of Sand

One of the most immediate pitfalls AI code generation stumbles into is versioning. As the CLAUDE.md example points out, Express 4 and Express 5 handle async errors fundamentally differently. Express 4 won’t automatically catch Promise rejections in your route handlers. Without explicit instruction, Claude might generate Express 5-style code that silently swallows errors in your production Express 4 app. The solution? Pin it down. Make the exact version of Express—and Node.js—a non-negotiable first entry in your CLAUDE.md.

Async Routes: The Silent Killers

Express 4 does NOT catch unhandled Promise rejections in routes.

This is, by all accounts, the single most common AI-generated Express.js bug. The code looks clean, the async/await syntax is there, but a thrown error in a Promise simply… disappears. The CLAUDE.md approach mandates a clear rule: all async route handlers must use either a strong asyncHandler wrapper (a common utility that wraps async functions to catch rejections and pass them to Express’s error handler) or explicit try/catch blocks. It’s about making the implicit explicit.

The Arity of Error Handling

Express identifies error-handling middleware by its function signature—specifically, its arity, the number of arguments it accepts. A standard middleware has three parameters (req, res, next). An error handler, however, must have four: (err, req, res, next). If Claude gets this wrong—and it does, especially when TypeScript types might be involved in the prompt—your meticulously crafted error handling logic will never even be invoked during an error. It’s a subtle, devastating oversight that a simple rule in your CLAUDE.md can prevent.

The Post-next() Paradox

Another common AI slip-up is the middleware that attempts to read from or write to req or res after calling next(). In synchronous middleware, this can lead to the infuriating “Cannot set headers after they are sent” error. In asynchronous flows, it’s even more complex. The request might have already moved on, or a response might have been finalized. The rule here is stark: once next() is called, the middleware’s job is done. Don’t touch req or res again.

Input Validation: A Shield Against Chaos

Sprawling, ad-hoc input validation scattered across handlers is a maintenance nightmare. CLAUDE.md enforces the use of a dedicated schema validation library—zod, joi, yup—and specifies which one. This ensures consistency and use the library’s built-in error reporting, preventing Claude from generating fragile, manual if checks that inevitably miss edge cases. The rule is absolute: all route handlers must validate request input before any business logic executes.

Router Architecture: Scoping Your Middleware

Claude has a tendency to default to app.use() for everything. This is fine for genuinely global concerns like body parsing or basic logging. But for feature-specific logic—like authentication that should only apply to a /users endpoint—applying it globally via app.use() creates security holes. CLAUDE.md dictates that feature-specific middleware should live on the feature’s dedicated router. This creates cleaner boundaries and prevents unintended access.

Body Parsing: Raw vs. JSON

This rule targets a specific, yet critical


🧬 Related Insights

Written by
Open Source Beat Editorial Team

Curated insights, explainers, and analysis from the editorial team.

Worth sharing?

Get the best Open Source stories of the week in your inbox — no noise, no spam.

Originally reported by Dev.to

Stay in the loop

The week's most important stories from Open Source Beat, delivered once a week.