Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions backend/nodejs/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
---
technology: Node.js
domain: backend
level: Senior/Architect
version: "24+"
tags: [best-practices, nodejs, architecture, design-patterns, clean-code, scalable-code, system-design]
ai_role: Senior Node.js Architecture Expert
last_updated: 2026-03-24
---

# 🟢 Node.js Architectural Patterns & Structuring

## ⚙️ Context & Scope
This document strictly enforces the deterministic architectural boundaries and structural patterns for Node.js backend systems.

```mermaid
graph TD
A["🟢 HTTP Interface Layer"] --> B["🔌 Controller / Route Layer"]
B --> C["⚙️ Core Business Logic (Services)"]
C --> D["🗄️ Data Access Layer (Repositories)"]
D --> E["💾 Persistent Storage"]

%% 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;
```

---

## 1. 🛑 Domain Coupling in Controllers
### ❌ Bad Practice
```javascript
// A controller handling HTTP request parsing, business logic, and database operations.
app.post('/api/users', async (req, res) => {
const { name, email } = req.body;
if (!email.includes('@')) return res.status(400).send('Invalid email');
const user = await db.collection('users').insertOne({ name, email, createdAt: new Date() });
await emailService.sendWelcome(email);
res.status(201).json(user);
});
```
### ⚠️ Problem
Tightly coupling business logic and database queries directly inside the HTTP transport layer (controllers) prevents unit testing and code reusability. It violates the Single Responsibility Principle, turning routes into unmaintainable monoliths.
### ✅ Best Practice
```javascript
// Controller delegating logic to the Service Layer
app.post('/api/users', async (req, res, next) => {
try {
const userDTO = await userService.createUser(req.body);
res.status(201).json(userDTO);
} catch (error) {
next(error);
}
});
```
### 🚀 Solution
Controllers MUST ONLY handle HTTP payload parsing and response formatting. Core business operations MUST be delegated to isolated Service classes.

## 2. 🗂️ Dependency Inversion
### ❌ Bad Practice
```javascript
// Hardcoding a database dependency directly into a service
const db = require('../config/database');

class UserService {
async getUser(id) {
return db.query('SELECT * FROM users WHERE id = ?', [id]);
}
}
```
### ⚠️ Problem
Hardcoding infrastructural dependencies directly into the service layer creates rigid code. It prevents dynamic swapping of database adapters and blocks the use of isolated mock databases during unit testing.
### ✅ Best Practice
```javascript
// Injecting dependencies through the constructor
class UserService {
constructor(userRepository) {
this.userRepository = userRepository;
}

async getUser(id) {
return this.userRepository.findById(id);
}
}
```
### 🚀 Solution
STRICTLY apply Dependency Injection. Services MUST receive infrastructural dependencies via their constructor, enabling decoupled layers and testability.

## 3. 🌐 Global State Mutation
### ❌ Bad Practice
```javascript
// Mutating global process.env during runtime
function setConfig(newPort) {
process.env.PORT = newPort;
}
```
### ⚠️ Problem
Mutating global state variables like `process.env` during application runtime creates unpredictable, non-deterministic side effects across all imported modules. This leads to untraceable bugs in asynchronous execution.
### ✅ Best Practice
```javascript
// Using an immutable configuration object
const config = Object.freeze({
port: process.env.PORT || 3000,
dbUrl: process.env.DATABASE_URL
});
```
### 🚀 Solution
Configuration objects MUST be locked and immutable after initialization. FORBID any runtime mutations to the global execution environment.
98 changes: 98 additions & 0 deletions backend/nodejs/security-best-practices.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
---
technology: Node.js
domain: backend
level: Senior/Architect
version: "24+"
tags: [security, best-practices, nodejs, clean-code, scalable-code, system-design]
ai_role: Senior Node.js Security Expert
last_updated: 2026-03-24
---

# 🟢 Node.js Security Best Practices

## ⚙️ Context & Scope
This document outlines the strict security configurations and anti-patterns that must be mitigated in a production Node.js environment.

---

## 1. 🛑 Prototype Pollution
### ❌ Bad Practice
```javascript
// Unsafe recursive object merging
function merge(target, source) {
for (let key in source) {
if (typeof source[key] === 'object') {
if (!target[key]) target[key] = {};
merge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
```
### ⚠️ Problem
Unsafe object merging allows attackers to overwrite properties on the global `Object.prototype` (e.g., via `__proto__`). This Prototype Pollution leads to application-wide configuration manipulation, privilege escalation, or Remote Code Execution (RCE).
### ✅ Best Practice
```javascript
// Safe merging utilizing null-prototype objects and property validation
function safeMerge(target, source) {
for (let key in source) {
if (key === '__proto__' || key === 'constructor' || key === 'prototype') continue;
if (typeof source[key] === 'object' && source[key] !== null) {
if (!target[key]) target[key] = {};
safeMerge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
```
### 🚀 Solution
STRICTLY filter reserved prototype keys (`__proto__`, `constructor`, `prototype`) during deep clone or merge operations. Alternatively, use robust, community-vetted libraries (like lodash.merge) that have built-in pollution defenses.

## 2. 🔏 Hardcoded Secrets
### ❌ Bad Practice
```javascript
// Exposing secrets in code
const dbClient = new Database('postgres://admin:supersecretpassword@db.internal:5432/mydb');
```
### ⚠️ Problem
Embedding plaintext passwords or API keys directly in source code guarantees a critical security breach if the repository is compromised. Hardcoded secrets persist in Git history forever and expose backend infrastructure to attackers.
### ✅ Best Practice
```javascript
// Utilizing environment variables safely
const dbUrl = process.env.DATABASE_URL;
if (!dbUrl) throw new Error('MANDATORY DATABASE_URL config is missing');

const dbClient = new Database(dbUrl);
```
### 🚀 Solution
MANDATORY injection of secrets via environment variables or secret management vaults (e.g., AWS Secrets Manager, HashiCorp Vault). NEVER commit sensitive material to version control.

## 3. 🛡️ Regular Expression Denial of Service (ReDoS)
### ❌ Bad Practice
```javascript
// A vulnerable regex pattern evaluated on user input
const emailRegex = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
app.post('/validate', (req, res) => {
const isValid = emailRegex.test(req.body.email);
res.send({ isValid });
});
```
### ⚠️ Problem
Using poorly optimized regular expressions with nested quantifiers (Catastrophic Backtracking) allows an attacker to send crafted payloads that block the Node.js event loop entirely. This causes a complete Denial of Service (ReDoS).
### ✅ Best Practice
```javascript
// Using a robust, vetted validation library
const validator = require('validator');

app.post('/validate', (req, res) => {
if (typeof req.body.email !== 'string') return res.status(400).send('Invalid format');
const isValid = validator.isEmail(req.body.email);
res.send({ isValid });
});
```
### 🚀 Solution
FORBID the use of complex, custom regular expressions on unconstrained user input. STRICTLY utilize established validation libraries and apply input length constraints before regex execution.
Loading