Awesome-omni-skill nodejs-backend-patterns
Build production-ready Node.js backend services with Express/Fastify, implementing middleware patterns, error handling, authentication, database integration, and API design best practices. Use when creating Node.js servers, REST APIs, GraphQL backends, or microservices architectures.
git clone https://github.com/diegosouzapw/awesome-omni-skill
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/development/nodejs-backend-patterns-ollieb89" ~/.claude/skills/diegosouzapw-awesome-omni-skill-nodejs-backend-patterns-80b3b6 && rm -rf "$T"
skills/development/nodejs-backend-patterns-ollieb89/SKILL.mdNode.js Backend Patterns
Comprehensive guidance for building scalable, maintainable, and production-ready Node.js backend applications with modern frameworks, architectural patterns, and best practices.
When to Use This Skill
- Building REST APIs or GraphQL servers
- Creating microservices with Node.js
- Implementing authentication and authorization
- Designing scalable backend architectures
- Setting up middleware and error handling
- Integrating databases (SQL and NoSQL)
- Building real-time applications with WebSockets
- Implementing background job processing
Core Frameworks
| Framework | Best For | Performance |
|---|---|---|
| Express.js | Flexibility, large ecosystem, quick prototyping | Good |
| Fastify | High performance, TypeScript support, schema validation | Excellent |
Quick Recommendation: Use Express.js for familiarity and ecosystem breadth. Use Fastify for performance-critical applications requiring built-in validation.
For detailed setup and patterns, see:
Architectural Patterns
Pattern 1: Layered Architecture
Structure:
src/ ├── controllers/ # Handle HTTP requests/responses ├── services/ # Business logic ├── repositories/ # Data access layer ├── models/ # Data models ├── middleware/ # Express/Fastify middleware ├── routes/ # Route definitions ├── utils/ # Helper functions ├── config/ # Configuration └── types/ # TypeScript types
Service Layer Pattern:
// services/user.service.ts import { UserRepository } from "../repositories/user.repository"; import { CreateUserDTO, UpdateUserDTO, User } from "../types/user.types"; import { NotFoundError, ValidationError } from "../utils/errors"; import bcrypt from "bcrypt"; export class UserService { constructor(private userRepository: UserRepository) {} async createUser(userData: CreateUserDTO): Promise<User> { const existingUser = await this.userRepository.findByEmail(userData.email); if (existingUser) { throw new ValidationError("Email already exists"); } const hashedPassword = await bcrypt.hash(userData.password, 10); const user = await this.userRepository.create({ ...userData, password: hashedPassword, }); const { password, ...userWithoutPassword } = user; return userWithoutPassword as User; } async getUserById(id: string): Promise<User> { const user = await this.userRepository.findById(id); if (!user) { throw new NotFoundError("User not found"); } const { password, ...userWithoutPassword } = user; return userWithoutPassword as User; } }
Pattern 2: Dependency Injection
DI Container:
// di-container.ts import { Pool } from "pg"; class Container { private instances = new Map<string, any>(); register<T>(key: string, factory: () => T): void { this.instances.set(key, factory); } resolve<T>(key: string): T { const factory = this.instances.get(key); if (!factory) { throw new Error(`No factory registered for ${key}`); } return factory(); } singleton<T>(key: string, factory: () => T): void { let instance: T; this.instances.set(key, () => { if (!instance) { instance = factory(); } return instance; }); } } export const container = new Container(); // Register dependencies container.singleton( "db", () => new Pool({ host: process.env.DB_HOST, port: parseInt(process.env.DB_PORT || "5432"), database: process.env.DB_NAME, user: process.env.DB_USER, password: process.env.DB_PASSWORD, max: 20, idleTimeoutMillis: 30000, connectionTimeoutMillis: 2000, }), ); container.singleton( "userRepository", () => new UserRepository(container.resolve("db")), ); container.singleton( "userService", () => new UserService(container.resolve("userRepository")), );
Middleware & Validation
Middleware patterns are framework-specific. See detailed guides:
Validation Schema Examples
Common validation patterns using Zod and Joi:
Database Patterns
PostgreSQL Connection Pool
// config/database.ts import { Pool, PoolConfig } from "pg"; const poolConfig: PoolConfig = { host: process.env.DB_HOST, port: parseInt(process.env.DB_PORT || "5432"), database: process.env.DB_NAME, user: process.env.DB_USER, password: process.env.DB_PASSWORD, max: 20, idleTimeoutMillis: 30000, connectionTimeoutMillis: 2000, }; export const pool = new Pool(poolConfig); pool.on("connect", () => console.log("Database connected")); pool.on("error", (err) => { console.error("Unexpected database error", err); process.exit(-1); }); export const closeDatabase = async () => { await pool.end(); };
MongoDB with Mongoose
// config/mongoose.ts import mongoose from "mongoose"; const connectDB = async () => { await mongoose.connect(process.env.MONGODB_URI!, { maxPoolSize: 10, serverSelectionTimeoutMS: 5000, socketTimeoutMS: 45000, }); console.log("MongoDB connected"); }; // Model example interface IUser extends Document { name: string; email: string; password: string; } const userSchema = new Schema<IUser>( { name: { type: String, required: true }, email: { type: String, required: true, unique: true }, password: { type: String, required: true }, }, { timestamps: true }, ); userSchema.index({ email: 1 }); export const User = model<IUser>("User", userSchema);
Transaction Pattern
// services/order.service.ts import { Pool } from "pg"; export class OrderService { constructor(private db: Pool) {} async createOrder(userId: string, items: any[]) { const client = await this.db.connect(); try { await client.query("BEGIN"); const orderResult = await client.query( "INSERT INTO orders (user_id, total) VALUES ($1, $2) RETURNING id", [userId, calculateTotal(items)], ); const orderId = orderResult.rows[0].id; for (const item of items) { await client.query( "INSERT INTO order_items (order_id, product_id, quantity, price) VALUES ($1, $2, $3, $4)", [orderId, item.productId, item.quantity, item.price], ); } await client.query("COMMIT"); return orderId; } catch (error) { await client.query("ROLLBACK"); throw error; } finally { client.release(); } } }
Authentication & Authorization
JWT Authentication Service
// services/auth.service.ts import jwt from "jsonwebtoken"; import bcrypt from "bcrypt"; import { UnauthorizedError } from "../utils/errors"; export class AuthService { constructor(private userRepository: UserRepository) {} async login(email: string, password: string) { const user = await this.userRepository.findByEmail(email); if (!user) { throw new UnauthorizedError("Invalid credentials"); } const isValid = await bcrypt.compare(password, user.password); if (!isValid) { throw new UnauthorizedError("Invalid credentials"); } const token = jwt.sign( { userId: user.id, email: user.email }, process.env.JWT_SECRET!, { expiresIn: "15m" }, ); const refreshToken = jwt.sign( { userId: user.id }, process.env.REFRESH_TOKEN_SECRET!, { expiresIn: "7d" }, ); return { token, refreshToken, user: { id: user.id, name: user.name, email: user.email }, }; } }
Caching Strategies
// utils/cache.ts import Redis from "ioredis"; const redis = new Redis({ host: process.env.REDIS_HOST, port: parseInt(process.env.REDIS_PORT || "6379"), }); export class CacheService { async get<T>(key: string): Promise<T | null> { const data = await redis.get(key); return data ? JSON.parse(data) : null; } async set(key: string, value: any, ttl?: number): Promise<void> { const serialized = JSON.stringify(value); if (ttl) { await redis.setex(key, ttl, serialized); } else { await redis.set(key, serialized); } } async invalidatePattern(pattern: string): Promise<void> { const keys = await redis.keys(pattern); if (keys.length > 0) { await redis.del(...keys); } } } // Cache decorator export function Cacheable(ttl: number = 300) { return function ( target: any, propertyKey: string, descriptor: PropertyDescriptor, ) { const originalMethod = descriptor.value; descriptor.value = async function (...args: any[]) { const cache = new CacheService(); const cacheKey = `${propertyKey}:${JSON.stringify(args)}`; const cached = await cache.get(cacheKey); if (cached) return cached; const result = await originalMethod.apply(this, args); await cache.set(cacheKey, result, ttl); return result; }; return descriptor; }; }
Security Best Practices
For comprehensive security guidelines, see:
Quick Security Summary
- Always use HTTPS in production
- Validate all inputs using Zod or Joi
- Hash passwords with bcrypt (cost 10-12)
- Use JWT with short expiration for access tokens
- Implement rate limiting on all endpoints
- Use security headers (Helmet)
- Configure CORS properly (no wildcards in production)
- Never expose stack traces or sensitive data in errors
- Use parameterized queries to prevent SQL injection
- Implement proper logging without sensitive data
Best Practices Summary
- Use TypeScript - Type safety prevents runtime errors
- Implement proper error handling - Use custom error classes
- Validate input - Use libraries like Zod or Joi
- Use environment variables - Never hardcode secrets
- Implement logging - Use structured logging (Pino, Winston)
- Add rate limiting - Prevent abuse
- Use HTTPS - Always in production
- Implement CORS properly - Don't use
in production* - Use dependency injection - Easier testing and maintenance
- Write tests - Unit, integration, and E2E tests
- Handle graceful shutdown - Clean up resources
- Use connection pooling - For databases
- Implement health checks - For monitoring
- Use compression - Reduce response size
- Monitor performance - Use APM tools
Testing Patterns
See
javascript-testing-patterns skill for comprehensive testing guidance.
Resources
- Node.js Best Practices: https://github.com/goldbergyoni/nodebestpractices
- Express.js Guide: https://expressjs.com/en/guide/
- Fastify Documentation: https://www.fastify.io/docs/
- TypeScript Node Starter: https://github.com/microsoft/TypeScript-NodeStarter