Node.js Code Review Patterns
SkillFiles & storageApplies Node.js-specific code review patterns for async I/O, streams, security, process management, and dependency hygiene. Use when reviewing Node.js server code, route handlers, middleware, or any JavaScript file alongside package.json without TypeScript. Triggers on sync I/O in request paths, missing stream backpressure, process.exit misuse, eval/exec injection risks, wildcard version ranges, missing lockfiles, EventEmitter cleanup gaps, and unvalidated environment variables at startup.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Node.js Code Review Patterns skill
What this skill tells your AI
The instructions your AI receives, as published by jamie-bitflight/claude_skills in plugins/development-harness/skills/code-review-nodejs/SKILL.md and read by ahel’s review.
Stack-specific rules loaded by dh:code-reviewer when package.json and *.js/*.mjs files are detected (without TypeScript).
Synchronous I/O in Request Path
fs.readFileSync,fs.writeFileSync,execSync,spawnSyncin any function called during request handling are blocking findings- All file system operations in server code must use the async variants or
fs/promises
// WRONG: blocks event loop
app.get("/config", (req, res) => {
const config = fs.readFileSync("./config.json", "utf8");
res.json(JSON.parse(config));
});
// RIGHT: non-blocking
app.get("/config", async (req, res) => {
const config = await fs.promises.readFile("./config.json", "utf8");
res.json(JSON.parse(config));
});
Stream Backpressure
- Piping streams without handling backpressure is a blocking finding for high-throughput paths
readable.pipe(writable)handles backpressure automatically — prefer it over manualdataevent listeners- Manual
dataevent listeners must checkwritable.write()return value and pause the readable when it returnsfalse
Process Exit
process.exit()is only acceptable in CLI entrypoints — it is a blocking finding in library code, route handlers, or middleware- Unhandled
process.on("uncaughtException")that callsprocess.exit()without logging the error is a blocking finding
Security
eval()is a blocking finding everywhere — no exceptionsnew Function(code)with user-controlledcodeis a blocking finding- Shell arguments constructed by string concatenation with user input before passing to
execorspawnare a blocking finding execFileis required overexecwhen calling external programs —execinvokes a shell and is vulnerable to injection- User-controlled values used as file paths must be validated against an allowed base directory (path traversal)
// WRONG: shell injection vector
exec(`convert ${userInput} output.png`);
// RIGHT: no shell, explicit args
execFile("convert", [userInput, "output.png"]);
Dependency Hygiene
*version ranges inpackage.jsonare a blocking finding — they produce non-reproducible installs^ranges are acceptable;~is preferred for stricter patch-level pinningpackage-lock.jsonoryarn.lockmust be committed — without a lockfile, versions are not reproducible in CI- Dev-only dependencies must be in
devDependencies, notdependencies
Event Emitter Cleanup
EventEmitter.on()listeners added in component/connection lifecycle must be removed when that lifecycle ends- Missing
removeListeneroroff()calls are a blocking finding when the emitter outlives the listener - Use
EventEmitter.once()for one-shot listeners to avoid manual cleanup
Environment Variables
- All required environment variables must be validated at startup, before the server begins accepting requests
process.env.SOME_VAR!without validation is a blocking finding — the app will fail with a confusing error at runtime rather than a clear startup message- Provide a
.env.examplefile listing all required variables — checked in, never containing real values
Anti-Patterns
// WRONG: missing error handling on EventEmitter
server.on("connection", (socket) => {
socket.on("data", handleData);
// missing: socket.on("end", cleanup) and removeListener
});
// WRONG: unvalidated env at use site
const apiKey = process.env.API_KEY;
fetch(url, { headers: { Authorization: apiKey } }); // null if unset
// RIGHT: validate at startup
if (!process.env.API_KEY) {
console.error("FATAL: API_KEY environment variable is required");
process.exit(1);
}
const apiKey = process.env.API_KEY;
Signals
- GitHub stars
- 66
- Forks
- 10
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
code-review-nodejs- Source
- github.com/jamie-bitflight/claude_skills