|
| 1 | +--- |
| 2 | +description: Vibe coding guidelines and architectural constraints for Node.js within the backend domain. |
| 3 | +technology: Node.js |
| 4 | +domain: backend |
| 5 | +level: Senior/Architect |
| 6 | +version: "24+" |
| 7 | +tags: [best-practices, clean-code, architecture-patterns, vibe-coding, cursor-rules, javascript, typescript, software-architecture, system-design, solid-principles, production-ready, programming-standards, node-js, design-patterns, scalable-code, windsurf-rules, ai-coding, fsd, ddd, enterprise-patterns] |
| 8 | +ai_role: Senior Node.js Architecture Expert |
| 9 | +last_updated: 2026-03-23 |
| 10 | +--- |
| 11 | + |
| 12 | +<div align="center"> |
| 13 | + <img src="https://raw.githubusercontent.com/tandpfun/skill-icons/main/icons/NodeJS-Dark.svg" width="100" alt="Node.js Logo"> |
| 14 | + |
| 15 | + # π’ Node.js Production-Ready Best Practices |
| 16 | +</div> |
| 17 | + |
| 18 | +--- |
| 19 | + |
| 20 | +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. |
| 21 | + |
| 22 | +# βοΈ Context & Scope |
| 23 | +- **Primary Goal:** Provide an uncompromising set of rules and architectural constraints for pure Node.js environments. |
| 24 | +- **Target Tooling:** AI-agents (Cursor, Windsurf, Copilot, Antigravity) and Senior Developers. |
| 25 | +- **Tech Stack Version:** Node.js 24+ |
| 26 | + |
| 27 | +> [!IMPORTANT] |
| 28 | +> **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. |
| 29 | +
|
| 30 | +--- |
| 31 | + |
| 32 | +## ποΈ Architecture & Component Isolation |
| 33 | + |
| 34 | +Node.js applications must use explicit module separation to handle logic appropriately. |
| 35 | + |
| 36 | +```mermaid |
| 37 | +graph TD |
| 38 | + A["π’ HTTP Server / Entry Point"] --> B["π Controllers / Routing"] |
| 39 | + B --> C["βοΈ Service Layer (Business Logic)"] |
| 40 | + C --> D["ποΈ Data Access Layer (Repositories)"] |
| 41 | + D --> E["πΎ Database / Cache"] |
| 42 | +
|
| 43 | + %% Added Design Token Styles for Mermaid Diagrams |
| 44 | + classDef default fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px,color:#000; |
| 45 | + classDef component fill:#e8f5e9,stroke:#4caf50,stroke-width:2px,color:#000; |
| 46 | + classDef layout fill:#f3e5f5,stroke:#9c27b0,stroke-width:2px,color:#000; |
| 47 | +
|
| 48 | + class A layout; |
| 49 | + class B component; |
| 50 | + class C component; |
| 51 | + class D component; |
| 52 | + class E layout; |
| 53 | +``` |
| 54 | + |
| 55 | +--- |
| 56 | + |
| 57 | +## 1. β‘ Blocking the Event Loop |
| 58 | +### β Bad Practice |
| 59 | +```javascript |
| 60 | +const crypto = require('crypto'); |
| 61 | +app.post('/hash', (req, res) => { |
| 62 | + const hash = crypto.pbkdf2Sync(req.body.password, 'salt', 100000, 64, 'sha512'); // Blocks the whole server |
| 63 | + res.send(hash); |
| 64 | +}); |
| 65 | +``` |
| 66 | +### β
Best Practice |
| 67 | +```javascript |
| 68 | +const crypto = require('crypto'); |
| 69 | +app.post('/hash', (req, res, next) => { |
| 70 | + crypto.pbkdf2(req.body.password, 'salt', 100000, 64, 'sha512', (err, derivedKey) => { |
| 71 | + if (err) return next(err); |
| 72 | + res.send(derivedKey.toString('hex')); |
| 73 | + }); |
| 74 | +}); |
| 75 | +``` |
| 76 | +### π Solution |
| 77 | +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. |
| 78 | + |
| 79 | +## 2. ποΈ Project Structure & Module Separation |
| 80 | +### β Bad Practice |
| 81 | +```text |
| 82 | +/server.js (Contains routes, DB connections, and logic all in one 1500-line file) |
| 83 | +``` |
| 84 | +### β
Best Practice |
| 85 | +```text |
| 86 | +/src |
| 87 | + /api (Controllers and routes) |
| 88 | + /services (Business logic) |
| 89 | + /models (Database schemas) |
| 90 | + /config (Environment and configurations) |
| 91 | + /utils (Helper functions) |
| 92 | +``` |
| 93 | +### π Solution |
| 94 | +Implement a multi-layered folder architecture. Strictly separate the HTTP transport layer (Routes/Controllers) from the Business Logic (Services) and Database operations. |
| 95 | + |
| 96 | +## 3. π‘οΈ Strict Environment Configuration |
| 97 | +### β Bad Practice |
| 98 | +```javascript |
| 99 | +const port = process.env.PORT || 3000; |
| 100 | +// Continuing application startup without validating required variables. |
| 101 | +``` |
| 102 | +### β
Best Practice |
| 103 | +```javascript |
| 104 | +const requiredEnv = ['DATABASE_URL', 'JWT_SECRET', 'PORT']; |
| 105 | +requiredEnv.forEach((name) => { |
| 106 | + if (!process.env[name]) { |
| 107 | + console.error(`Environment variable ${name} is missing.`); |
| 108 | + process.exit(1); |
| 109 | + } |
| 110 | +}); |
| 111 | +``` |
| 112 | +### π Solution |
| 113 | +Fail fast. Validate all necessary environment variables upon application startup to prevent fatal runtime errors later in execution. |
| 114 | + |
| 115 | +## 4. π Error Handling with Custom Classes |
| 116 | +### β Bad Practice |
| 117 | +```javascript |
| 118 | +if (!user) throw new Error('User not found'); |
| 119 | +``` |
| 120 | +### β
Best Practice |
| 121 | +```javascript |
| 122 | +class AppError extends Error { |
| 123 | + constructor(message, statusCode) { |
| 124 | + super(message); |
| 125 | + this.statusCode = statusCode; |
| 126 | + this.isOperational = true; // Distinguish between operational and programming errors |
| 127 | + } |
| 128 | +} |
| 129 | +if (!user) throw new AppError('User not found', 404); |
| 130 | +``` |
| 131 | +### π Solution |
| 132 | +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. |
| 133 | + |
| 134 | +## 5. ποΈ Handling Uncaught Exceptions & Rejections |
| 135 | +### β Bad Practice |
| 136 | +// Ignoring process-level events, allowing the app to run in an unpredictable state after an error. |
| 137 | +### β
Best Practice |
| 138 | +```javascript |
| 139 | +process.on('uncaughtException', (err) => { |
| 140 | + logger.error('UNCAUGHT EXCEPTION! Shutting down...', err); |
| 141 | + process.exit(1); |
| 142 | +}); |
| 143 | + |
| 144 | +process.on('unhandledRejection', (err) => { |
| 145 | + logger.error('UNHANDLED REJECTION! Shutting down...', err); |
| 146 | + server.close(() => process.exit(1)); |
| 147 | +}); |
| 148 | +``` |
| 149 | +### π Solution |
| 150 | +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. |
| 151 | + |
| 152 | +## 6. π Hiding Sensitive Headers |
| 153 | +### β Bad Practice |
| 154 | +// Sending default headers that expose the framework, like `X-Powered-By: Express`. |
| 155 | +### β
Best Practice |
| 156 | +```javascript |
| 157 | +// Example using Express + Helmet, but applies generically to HTTP responses |
| 158 | +const helmet = require('helmet'); |
| 159 | +app.use(helmet()); |
| 160 | +``` |
| 161 | +### π Solution |
| 162 | +Sanitize outgoing HTTP headers to prevent information leakage about the server infrastructure. |
| 163 | + |
| 164 | +## 7. β±οΈ Implementing Graceful Shutdown |
| 165 | +### β Bad Practice |
| 166 | +// Application crashes abruptly during deployments, interrupting active user requests and corrupting database transactions. |
| 167 | +### β
Best Practice |
| 168 | +```javascript |
| 169 | +process.on('SIGTERM', () => { |
| 170 | + console.info('SIGTERM signal received. Closing HTTP server.'); |
| 171 | + server.close(() => { |
| 172 | + console.log('HTTP server closed.'); |
| 173 | + mongoose.connection.close(false, () => { |
| 174 | + console.log('Database connection closed.'); |
| 175 | + process.exit(0); |
| 176 | + }); |
| 177 | + }); |
| 178 | +}); |
| 179 | +``` |
| 180 | +### π Solution |
| 181 | +Listen for termination signals (`SIGTERM`, `SIGINT`). Finish processing ongoing HTTP requests and safely close database connections before exiting the Node.js process. |
| 182 | + |
| 183 | +## 8. π Input Validation and Sanitization |
| 184 | +### β Bad Practice |
| 185 | +```javascript |
| 186 | +// Blindly trusting user input |
| 187 | +const user = await db.query(`SELECT * FROM users WHERE email = '${req.body.email}'`); |
| 188 | +``` |
| 189 | +### β
Best Practice |
| 190 | +```javascript |
| 191 | +// Utilizing parameterized queries and a validation library like Joi or Zod |
| 192 | +const schema = Joi.object({ email: Joi.string().email().required() }); |
| 193 | +const { error, value } = schema.validate(req.body); |
| 194 | + |
| 195 | +if (error) throw new AppError('Invalid input', 400); |
| 196 | +const user = await db.query('SELECT * FROM users WHERE email = $1', [value.email]); |
| 197 | +``` |
| 198 | +### π Solution |
| 199 | +Never trust external data. Validate input strictly using schema definitions and always utilize parameterized queries or an ORM to prevent SQL/NoSQL Injection attacks. |
| 200 | + |
| 201 | +## 9. π Utilizing Worker Threads for Heavy Tasks |
| 202 | +### β Bad Practice |
| 203 | +```javascript |
| 204 | +// Processing a massive image buffer directly on the main event loop |
| 205 | +function processImage(buffer) { |
| 206 | + // heavy sync computation taking 500ms... |
| 207 | +} |
| 208 | +``` |
| 209 | +### β
Best Practice |
| 210 | +```javascript |
| 211 | +const { Worker } = require('worker_threads'); |
| 212 | + |
| 213 | +function processImageAsync(buffer) { |
| 214 | + return new Promise((resolve, reject) => { |
| 215 | + const worker = new Worker('./imageProcessor.js', { workerData: buffer }); |
| 216 | + worker.on('message', resolve); |
| 217 | + worker.on('error', reject); |
| 218 | + }); |
| 219 | +} |
| 220 | +``` |
| 221 | +### π Solution |
| 222 | +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. |
| 223 | + |
| 224 | +## 10. π Centralized and Structured Logging |
| 225 | +### β Bad Practice |
| 226 | +```javascript |
| 227 | +console.log('User logged in', userId); |
| 228 | +``` |
| 229 | +### β
Best Practice |
| 230 | +```javascript |
| 231 | +const winston = require('winston'); |
| 232 | +const logger = winston.createLogger({ |
| 233 | + level: 'info', |
| 234 | + format: winston.format.json(), |
| 235 | + transports: [new winston.transports.Console()], |
| 236 | +}); |
| 237 | + |
| 238 | +logger.info('User logged in', { userId, timestamp: new Date().toISOString() }); |
| 239 | +``` |
| 240 | +### π Solution |
| 241 | +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). |
| 242 | + |
| 243 | +<br> |
| 244 | + |
| 245 | +<div align="center"> |
| 246 | + <b>Enforce these Core Node.js constraints to ensure a highly scalable, stable, and performant backend system! π’</b> |
| 247 | +</div> |
0 commit comments