nodejs-best-practices
Node.js development principles and decision-making. Framework selection, async patterns, security, and architecture. Teaches thinking, not copying.
Install
npx skills add https://github.com/VoDaiLocz/kilo-kit-mcp/tree/main/skills/engineering/nodejs-best-practices
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install vodailocz-kilo-kit-mcp@llmmart
git clone https://github.com/VoDaiLocz/kilo-kit-mcp.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole vodailocz/kilo-kit-mcp collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Node.js Best Practices
Principles and decision-making for Node.js development in 2025. Learn to THINK, not memorize code patterns.
⚠️ How to Use This Skill
This skill teaches decision-making principles, not fixed code to copy.
- ASK user for preferences when unclear
- Choose framework/pattern based on CONTEXT
- Don't default to same solution every time
1. Framework Selection (2025)
Decision Tree
What are you building?
│
├── Edge/Serverless (Cloudflare, Vercel)
│ └── Hono (zero-dependency, ultra-fast cold starts)
│
├── High Performance API
│ └── Fastify (2-3x faster than Express)
│
├── Enterprise/Team familiarity
│ └── NestJS (structured, DI, decorators)
│
├── Legacy/Stable/Maximum ecosystem
│ └── Express (mature, most middleware)
│
└── Full-stack with frontend
└── Next.js API Routes or tRPC
Comparison Principles
| Factor | Hono | Fastify | Express |
|---|---|---|---|
| Best for | Edge, serverless | Performance | Legacy, learning |
| Cold start | Fastest | Fast | Moderate |
| Ecosystem | Growing | Good | Largest |
| TypeScript | Native | Excellent | Good |
| Learning curve | Low | Medium | Low |
Selection Questions to Ask:
- What's the deployment target?
- Is cold start time critical?
- Does team have existing experience?
- Is there legacy code to maintain?
2. Runtime Considerations (2025)
Native TypeScript
Node.js 22+: --experimental-strip-types
├── Run .ts files directly
├── No build step needed for simple projects
└── Consider for: scripts, simple APIs
Module System Decision
ESM (import/export)
├── Modern standard
├── Better tree-shaking
├── Async module loading
└── Use for: new projects
CommonJS (require)
├── Legacy compatibility
├── More npm packages support
└── Use for: existing codebases, some edge cases
Runtime Selection
| Runtime | Best For |
|---|---|
| Node.js | General purpose, largest ecosystem |
| Bun | Performance, built-in bundler |
| Deno | Security-first, built-in TypeScript |
3. Architecture Principles
Layered Structure Concept
Request Flow:
│
├── Controller/Route Layer
│ ├── Handles HTTP specifics
│ ├── Input validation at boundary
│ └── Calls service layer
│
├── Service Layer
│ ├── Business logic
│ ├── Framework-agnostic
│ └── Calls repository layer
│
└── Repository Layer
├── Data access only
├── Database queries
└── ORM interactions
Why This Matters:
- Testability: Mock layers independently
- Flexibility: Swap database without touching business logic
- Clarity: Each layer has single responsibility
When to Simplify:
- Small scripts → Single file OK
- Prototypes → Less structure acceptable
- Always ask: "Will this grow?"
4. Error Handling Principles
Centralized Error Handling
Pattern:
├── Create custom error classes
├── Throw from any layer
├── Catch at top level (middleware)
└── Format consistent response
Error Response Philosophy
Client gets:
├── Appropriate HTTP status
├── Error code for programmatic handling
├── User-friendly message
└── NO internal details (security!)
Logs get:
├── Full stack trace
├── Request context
├── User ID (if applicable)
└── Timestamp
Status Code Selection
| Situation | Status | When |
|---|---|---|
| Bad input | 400 | Client sent invalid data |
| No auth | 401 | Missing or invalid credentials |
| No permission | 403 | Valid auth, but not allowed |
| Not found | 404 | Resource doesn't exist |
| Conflict | 409 | Duplicate or state conflict |
| Validation | 422 | Schema valid but business rules fail |
| Server error | 500 | Our fault, log everything |
5. Async Patterns Principles
When to Use Each
| Pattern | Use When |
|---|---|
async/await |
Sequential async operations |
Promise.all |
Parallel independent operations |
Promise.allSettled |
Parallel where some can fail |
Promise.race |
Timeout or first response wins |
Event Loop Awareness
I/O-bound (async helps):
├── Database queries
├── HTTP requests
├── File system
└── Network operations
CPU-bound (async doesn't help):
├── Crypto operations
├── Image processing
├── Complex calculations
└── → Use worker threads or offload
Avoiding Event Loop Blocking
- Never use sync methods in production (fs.readFileSync, etc.)
- Offload CPU-intensive work
- Use streaming for large data
6. Validation Principles
Validate at Boundaries
Where to validate:
├── API entry point (request body/params)
├── Before database operations
├── External data (API responses, file uploads)
└── Environment variables (startup)
Validation Library Selection
| Library | Best For |
|---|---|
| Zod | TypeScript first, inference |
| Valibot | Smaller bundle (tree-shakeable) |
| ArkType | Performance critical |
| Yup | Existing React Form usage |
Validation Philosophy
- Fail fast: Validate early
- Be specific: Clear error messages
- Don't trust: Even "internal" data
7. Security Principles
Security Checklist (Not Code)
- Input validation: All inputs validated
- Parameterized queries: No string concatenation for SQL
- Password hashing: bcrypt or argon2
- JWT verification: Always verify signature and expiry
- Rate limiting: Protect from abuse
- Security headers: Helmet.js or equivalent
- HTTPS: Everywhere in production
- CORS: Properly configured
- Secrets: Environment variables only
- Dependencies: Regularly audited
Security Mindset
Trust nothing:
├── Query params → validate
├── Request body → validate
├── Headers → verify
├── Cookies → validate
├── File uploads → scan
└── External APIs → validate response
8. Testing Principles
Test Strategy Selection
| Type | Purpose | Tools |
|---|---|---|
| Unit | Business logic | node:test, Vitest |
| Integration | API endpoints | Supertest |
| E2E | Full flows | Playwright |
What to Test (Priorities)
- Critical paths: Auth, payments, core business
- Edge cases: Empty inputs, boundaries
- Error handling: What happens when things fail?
- Not worth testing: Framework code, trivial getters
Built-in Test Runner (Node.js 22+)
node --test src/**/*.test.ts
├── No external dependency
├── Good coverage reporting
└── Watch mode available
10. Anti-Patterns to Avoid
❌ DON'T:
- Use Express for new edge projects (use Hono)
- Use sync methods in production code
- Put business logic in controllers
- Skip input validation
- Hardcode secrets
- Trust external data without validation
- Block event loop with CPU work
✅ DO:
- Choose framework based on context
- Ask user for preferences when unclear
- Use layered architecture for growing projects
- Validate all inputs
- Use environment variables for secrets
- Profile before optimizing
11. Decision Checklist
Before implementing:
- Asked user about stack preference?
- Chosen framework for THIS context? (not just default)
- Considered deployment target?
- Planned error handling strategy?
- Identified validation points?
- Considered security requirements?
Remember: Node.js best practices are about decision-making, not memorizing patterns. Every project deserves fresh consideration based on its requirements.
Files (kilo-kit-mcp)
-
SKILL.md 8.1 KB
--- name: nodejs-best-practices description: Node.js development principles and decision-making. Framework selection, async patterns, security, and architecture. Teaches thinking, not copying. allowed-tools: Read, Write, Edit, Glob, Grep --- # Node.js Best Practices > Principles and decision-making for Node.js development in 2025. > **Learn to THINK, not memorize code patterns.** --- ## ⚠️ How to Use This Skill This skill teaches **decision-making principles**, not fixed code to copy. - ASK user for preferences when unclear - Choose framework/pattern based on CONTEXT - Don't default to same solution every time --- ## 1. Framework Selection (2025) ### Decision Tree ``` What are you building? │ ├── Edge/Serverless (Cloudflare, Vercel) │ └── Hono (zero-dependency, ultra-fast cold starts) │ ├── High Performance API │ └── Fastify (2-3x faster than Express) │ ├── Enterprise/Team familiarity │ └── NestJS (structured, DI, decorators) │ ├── Legacy/Stable/Maximum ecosystem │ └── Express (mature, most middleware) │ └── Full-stack with frontend └── Next.js API Routes or tRPC ``` ### Comparison Principles | Factor | Hono | Fastify | Express | |--------|------|---------|---------| | **Best for** | Edge, serverless | Performance | Legacy, learning | | **Cold start** | Fastest | Fast | Moderate | | **Ecosystem** | Growing | Good | Largest | | **TypeScript** | Native | Excellent | Good | | **Learning curve** | Low | Medium | Low | ### Selection Questions to Ask: 1. What's the deployment target? 2. Is cold start time critical? 3. Does team have existing experience? 4. Is there legacy code to maintain? --- ## 2. Runtime Considerations (2025) ### Native TypeScript ``` Node.js 22+: --experimental-strip-types ├── Run .ts files directly ├── No build step needed for simple projects └── Consider for: scripts, simple APIs ``` ### Module System Decision ``` ESM (import/export) ├── Modern standard ├── Better tree-shaking ├── Async module loading └── Use for: new projects CommonJS (require) ├── Legacy compatibility ├── More npm packages support └── Use for: existing codebases, some edge cases ``` ### Runtime Selection | Runtime | Best For | |---------|----------| | **Node.js** | General purpose, largest ecosystem | | **Bun** | Performance, built-in bundler | | **Deno** | Security-first, built-in TypeScript | --- ## 3. Architecture Principles ### Layered Structure Concept ``` Request Flow: │ ├── Controller/Route Layer │ ├── Handles HTTP specifics │ ├── Input validation at boundary │ └── Calls service layer │ ├── Service Layer │ ├── Business logic │ ├── Framework-agnostic │ └── Calls repository layer │ └── Repository Layer ├── Data access only ├── Database queries └── ORM interactions ``` ### Why This Matters: - **Testability**: Mock layers independently - **Flexibility**: Swap database without touching business logic - **Clarity**: Each layer has single responsibility ### When to Simplify: - Small scripts → Single file OK - Prototypes → Less structure acceptable - Always ask: "Will this grow?" --- ## 4. Error Handling Principles ### Centralized Error Handling ``` Pattern: ├── Create custom error classes ├── Throw from any layer ├── Catch at top level (middleware) └── Format consistent response ``` ### Error Response Philosophy ``` Client gets: ├── Appropriate HTTP status ├── Error code for programmatic handling ├── User-friendly message └── NO internal details (security!) Logs get: ├── Full stack trace ├── Request context ├── User ID (if applicable) └── Timestamp ``` ### Status Code Selection | Situation | Status | When | |-----------|--------|------| | Bad input | 400 | Client sent invalid data | | No auth | 401 | Missing or invalid credentials | | No permission | 403 | Valid auth, but not allowed | | Not found | 404 | Resource doesn't exist | | Conflict | 409 | Duplicate or state conflict | | Validation | 422 | Schema valid but business rules fail | | Server error | 500 | Our fault, log everything | --- ## 5. Async Patterns Principles ### When to Use Each | Pattern | Use When | |---------|----------| | `async/await` | Sequential async operations | | `Promise.all` | Parallel independent operations | | `Promise.allSettled` | Parallel where some can fail | | `Promise.race` | Timeout or first response wins | ### Event Loop Awareness ``` I/O-bound (async helps): ├── Database queries ├── HTTP requests ├── File system └── Network operations CPU-bound (async doesn't help): ├── Crypto operations ├── Image processing ├── Complex calculations └── → Use worker threads or offload ``` ### Avoiding Event Loop Blocking - Never use sync methods in production (fs.readFileSync, etc.) - Offload CPU-intensive work - Use streaming for large data --- ## 6. Validation Principles ### Validate at Boundaries ``` Where to validate: ├── API entry point (request body/params) ├── Before database operations ├── External data (API responses, file uploads) └── Environment variables (startup) ``` ### Validation Library Selection | Library | Best For | |---------|----------| | **Zod** | TypeScript first, inference | | **Valibot** | Smaller bundle (tree-shakeable) | | **ArkType** | Performance critical | | **Yup** | Existing React Form usage | ### Validation Philosophy - Fail fast: Validate early - Be specific: Clear error messages - Don't trust: Even "internal" data --- ## 7. Security Principles ### Security Checklist (Not Code) - [ ] **Input validation**: All inputs validated - [ ] **Parameterized queries**: No string concatenation for SQL - [ ] **Password hashing**: bcrypt or argon2 - [ ] **JWT verification**: Always verify signature and expiry - [ ] **Rate limiting**: Protect from abuse - [ ] **Security headers**: Helmet.js or equivalent - [ ] **HTTPS**: Everywhere in production - [ ] **CORS**: Properly configured - [ ] **Secrets**: Environment variables only - [ ] **Dependencies**: Regularly audited ### Security Mindset ``` Trust nothing: ├── Query params → validate ├── Request body → validate ├── Headers → verify ├── Cookies → validate ├── File uploads → scan └── External APIs → validate response ``` --- ## 8. Testing Principles ### Test Strategy Selection | Type | Purpose | Tools | |------|---------|-------| | **Unit** | Business logic | node:test, Vitest | | **Integration** | API endpoints | Supertest | | **E2E** | Full flows | Playwright | ### What to Test (Priorities) 1. **Critical paths**: Auth, payments, core business 2. **Edge cases**: Empty inputs, boundaries 3. **Error handling**: What happens when things fail? 4. **Not worth testing**: Framework code, trivial getters ### Built-in Test Runner (Node.js 22+) ``` node --test src/**/*.test.ts ├── No external dependency ├── Good coverage reporting └── Watch mode available ``` --- ## 10. Anti-Patterns to Avoid ### ❌ DON'T: - Use Express for new edge projects (use Hono) - Use sync methods in production code - Put business logic in controllers - Skip input validation - Hardcode secrets - Trust external data without validation - Block event loop with CPU work ### ✅ DO: - Choose framework based on context - Ask user for preferences when unclear - Use layered architecture for growing projects - Validate all inputs - Use environment variables for secrets - Profile before optimizing --- ## 11. Decision Checklist Before implementing: - [ ] **Asked user about stack preference?** - [ ] **Chosen framework for THIS context?** (not just default) - [ ] **Considered deployment target?** - [ ] **Planned error handling strategy?** - [ ] **Identified validation points?** - [ ] **Considered security requirements?** --- > **Remember**: Node.js best practices are about decision-making, not memorizing patterns. Every project deserves fresh consideration based on its requirements.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.