| technology | Node.js | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| domain | backend | ||||||||||||||||||||
| level | Senior/Architect | ||||||||||||||||||||
| version | 24+ | ||||||||||||||||||||
| tags |
|
||||||||||||||||||||
| ai_role | Senior Node.js Architecture Expert | ||||||||||||||||||||
| last_updated | 2026-03-23 |
This document establishes best practices for building and maintaining Node.js applications. These constraints guarantee a scalable, highly secure, and clean architecture suitable for an enterprise-level, production-ready backend.
- Primary Goal: Provide an uncompromising set of rules and architectural constraints for pure Node.js environments.
- Target Tooling: AI-agents (Cursor, Windsurf, Copilot, Antigravity) and Senior Developers.
- Tech Stack Version: Node.js 24+
Important
Architectural Contract: Code must be completely asynchronous. Absolutely avoid synchronous blocking methods like readFileSync or crypto.pbkdf2Sync on the main thread. Delegate heavy computational tasks to Worker Threads or separate microservices to keep the event loop non-blocking.
Node.js applications must use explicit module separation to handle logic appropriately.
graph TD
A["🟢 HTTP Server / Entry Point"] --> B["🔌 Controllers / Routing"]
B --> C["⚙️ Service Layer (Business Logic)"]
C --> D["🗄️ Data Access Layer (Repositories)"]
D --> E["💾 Database / Cache"]
%% Added Design Token Styles for Mermaid Diagrams
classDef default fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px,color:#000;
classDef component fill:#e8f5e9,stroke:#4caf50,stroke-width:2px,color:#000;
classDef layout fill:#f3e5f5,stroke:#9c27b0,stroke-width:2px,color:#000;
class A layout;
class B component;
class C component;
class D component;
class E layout;
const crypto = require('crypto');
app.post('/hash', (req, res) => {
const hash = crypto.pbkdf2Sync(req.body.password, 'salt', 100000, 64, 'sha512'); // Blocks the whole server
res.send(hash);
});const crypto = require('crypto');
app.post('/hash', (req, res, next) => {
crypto.pbkdf2(req.body.password, 'salt', 100000, 64, 'sha512', (err, derivedKey) => {
if (err) return next(err);
res.send(derivedKey.toString('hex'));
});
});Never use synchronous methods (*Sync) on the main thread for crypto, I/O, or heavy calculations. Always use asynchronous callbacks or Promises to prevent blocking the Event Loop.
Insecure or unoptimized implementation that can cause performance bottlenecks, maintainability issues, or security vulnerabilities. It deviates from modern deterministic standards, making the code harder for AI Agents and Senior Developers to parse and safely extend.
/server.js (Contains routes, DB connections, and logic all in one 1500-line file)
/src
/api (Controllers and routes)
/services (Business logic)
/models (Database schemas)
/config (Environment and configurations)
/utils (Helper functions)
Implement a multi-layered folder architecture. Strictly separate the HTTP transport layer (Routes/Controllers) from the Business Logic (Services) and Database operations.
Insecure or unoptimized implementation that can cause performance bottlenecks, maintainability issues, or security vulnerabilities. It deviates from modern deterministic standards, making the code harder for AI Agents and Senior Developers to parse and safely extend.
const port = process.env.PORT || 3000;
// Continuing application startup without validating required variables.const requiredEnv = ['DATABASE_URL', 'JWT_SECRET', 'PORT'];
requiredEnv.forEach((name) => {
if (!process.env[name]) {
console.error(`Environment variable ${name} is missing.`);
process.exit(1);
}
});Fail fast. Validate all necessary environment variables upon application startup to prevent fatal runtime errors later in execution.
Insecure or unoptimized implementation that can cause performance bottlenecks, maintainability issues, or security vulnerabilities. It deviates from modern deterministic standards, making the code harder for AI Agents and Senior Developers to parse and safely extend.
if (!user) throw new Error('User not found');class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.isOperational = true; // Distinguish between operational and programming errors
}
}
if (!user) throw new AppError('User not found', 404);Extend the built-in Error object to create custom operational errors. This allows your global error handler to safely log and return predictable HTTP status codes without crashing the application.
Insecure or unoptimized implementation that can cause performance bottlenecks, maintainability issues, or security vulnerabilities. It deviates from modern deterministic standards, making the code harder for AI Agents and Senior Developers to parse and safely extend.
// Ignoring process-level events, allowing the app to run in an unpredictable state after an error.
process.on('uncaughtException', (err) => {
logger.error('UNCAUGHT EXCEPTION! Shutting down...', err);
process.exit(1);
});
process.on('unhandledRejection', (err) => {
logger.error('UNHANDLED REJECTION! Shutting down...', err);
server.close(() => process.exit(1));
});Always capture uncaughtException and unhandledRejection. Log the fatal error immediately and shut down the process safely. Rely on a process manager (like PM2 or Kubernetes) to restart the container.
Insecure or unoptimized implementation that can cause performance bottlenecks, maintainability issues, or security vulnerabilities. It deviates from modern deterministic standards, making the code harder for AI Agents and Senior Developers to parse and safely extend.
// Sending default headers that expose the framework, like X-Powered-By: Express.
// Example using Express + Helmet, but applies generically to HTTP responses
const helmet = require('helmet');
app.use(helmet());Sanitize outgoing HTTP headers to prevent information leakage about the server infrastructure.
Insecure or unoptimized implementation that can cause performance bottlenecks, maintainability issues, or security vulnerabilities. It deviates from modern deterministic standards, making the code harder for AI Agents and Senior Developers to parse and safely extend.
// Application crashes abruptly during deployments, interrupting active user requests and corrupting database transactions.
process.on('SIGTERM', () => {
console.info('SIGTERM signal received. Closing HTTP server.');
server.close(() => {
console.log('HTTP server closed.');
mongoose.connection.close(false, () => {
console.log('Database connection closed.');
process.exit(0);
});
});
});Listen for termination signals (SIGTERM, SIGINT). Finish processing ongoing HTTP requests and safely close database connections before exiting the Node.js process.
Insecure or unoptimized implementation that can cause performance bottlenecks, maintainability issues, or security vulnerabilities. It deviates from modern deterministic standards, making the code harder for AI Agents and Senior Developers to parse and safely extend.
// Blindly trusting user input
const user = await db.query(`SELECT * FROM users WHERE email = '${req.body.email}'`);// Utilizing parameterized queries and a validation library like Joi or Zod
const schema = Joi.object({ email: Joi.string().email().required() });
const { error, value } = schema.validate(req.body);
if (error) throw new AppError('Invalid input', 400);
const user = await db.query('SELECT * FROM users WHERE email = $1', [value.email]);Never trust external data. Validate input strictly using schema definitions and always utilize parameterized queries or an ORM to prevent SQL/NoSQL Injection attacks.
Insecure or unoptimized implementation that can cause performance bottlenecks, maintainability issues, or security vulnerabilities. It deviates from modern deterministic standards, making the code harder for AI Agents and Senior Developers to parse and safely extend.
// Processing a massive image buffer directly on the main event loop
function processImage(buffer) {
// heavy sync computation taking 500ms...
}const { Worker } = require('worker_threads');
function processImageAsync(buffer) {
return new Promise((resolve, reject) => {
const worker = new Worker('./imageProcessor.js', { workerData: buffer });
worker.on('message', resolve);
worker.on('error', reject);
});
}Offload CPU-intensive operations (image processing, video encoding, heavy cryptographic tasks) to Node.js worker_threads to keep the primary event loop highly responsive for API requests.
Insecure or unoptimized implementation that can cause performance bottlenecks, maintainability issues, or security vulnerabilities. It deviates from modern deterministic standards, making the code harder for AI Agents and Senior Developers to parse and safely extend.
console.log('User logged in', userId);const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [new winston.transports.Console()],
});
logger.info('User logged in', { userId, timestamp: new Date().toISOString() });Avoid console.log. Use a sophisticated logging library (like Pino or Winston) to generate structured, JSON-formatted logs that are easily ingested by external monitoring systems (Datadog, ELK).
Insecure or unoptimized implementation that can cause performance bottlenecks, maintainability issues, or security vulnerabilities. It deviates from modern deterministic standards, making the code harder for AI Agents and Senior Developers to parse and safely extend.