Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Part of a 72-plugin marketplace with 112 AI agents and 146 skills for Claude Code development automation.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/advanced-patterns.md
1# Node.js Advanced Patterns23Advanced patterns for dependency injection, database integration, authentication, caching, and API response formatting.45## Dependency Injection67### DI Container89```typescript10// di-container.ts11import { Pool } from "pg";12import { UserRepository } from "./repositories/user.repository";13import { UserService } from "./services/user.service";14import { UserController } from "./controllers/user.controller";15import { AuthService } from "./services/auth.service";1617class Container {18private instances = new Map<string, any>();1920register<T>(key: string, factory: () => T): void {21this.instances.set(key, factory);22}2324resolve<T>(key: string): T {25const factory = this.instances.get(key);26if (!factory) {27throw new Error(`No factory registered for ${key}`);28}29return factory();30}3132singleton<T>(key: string, factory: () => T): void {33let instance: T;34this.instances.set(key, () => {35if (!instance) {36instance = factory();37}38return instance;39});40}41}4243export const container = new Container();4445// Register dependencies46container.singleton(47"db",48() =>49new Pool({50host: process.env.DB_HOST,51port: parseInt(process.env.DB_PORT || "5432"),52database: process.env.DB_NAME,53user: process.env.DB_USER,54password: process.env.DB_PASSWORD,55max: 20,56idleTimeoutMillis: 30000,57connectionTimeoutMillis: 2000,58}),59);6061container.singleton(62"userRepository",63() => new UserRepository(container.resolve("db")),64);6566container.singleton(67"userService",68() => new UserService(container.resolve("userRepository")),69);7071container.register(72"userController",73() => new UserController(container.resolve("userService")),74);7576container.singleton(77"authService",78() => new AuthService(container.resolve("userRepository")),79);80```8182## Database Patterns8384### PostgreSQL with Connection Pool8586```typescript87// config/database.ts88import { Pool, PoolConfig } from "pg";8990const poolConfig: PoolConfig = {91host: process.env.DB_HOST,92port: parseInt(process.env.DB_PORT || "5432"),93database: process.env.DB_NAME,94user: process.env.DB_USER,95password: process.env.DB_PASSWORD,96max: 20,97idleTimeoutMillis: 30000,98connectionTimeoutMillis: 2000,99};100101export const pool = new Pool(poolConfig);102103// Test connection104pool.on("connect", () => {105console.log("Database connected");106});107108pool.on("error", (err) => {109console.error("Unexpected database error", err);110process.exit(-1);111});112113// Graceful shutdown114export const closeDatabase = async () => {115await pool.end();116console.log("Database connection closed");117};118```119120### MongoDB with Mongoose121122```typescript123// config/mongoose.ts124import mongoose from "mongoose";125126const connectDB = async () => {127try {128await mongoose.connect(process.env.MONGODB_URI!, {129maxPoolSize: 10,130serverSelectionTimeoutMS: 5000,131socketTimeoutMS: 45000,132});133134console.log("MongoDB connected");135} catch (error) {136console.error("MongoDB connection error:", error);137process.exit(1);138}139};140141mongoose.connection.on("disconnected", () => {142console.log("MongoDB disconnected");143});144145mongoose.connection.on("error", (err) => {146console.error("MongoDB error:", err);147});148149export { connectDB };150151// Model example152import { Schema, model, Document } from "mongoose";153154interface IUser extends Document {155name: string;156email: string;157password: string;158createdAt: Date;159updatedAt: Date;160}161162const userSchema = new Schema<IUser>(163{164name: { type: String, required: true },165email: { type: String, required: true, unique: true },166password: { type: String, required: true },167},168{169timestamps: true,170},171);172173// Indexes174userSchema.index({ email: 1 });175176export const User = model<IUser>("User", userSchema);177```178179### Transaction Pattern180181```typescript182// services/order.service.ts183import { Pool } from "pg";184185export class OrderService {186constructor(private db: Pool) {}187188async createOrder(userId: string, items: any[]) {189const client = await this.db.connect();190191try {192await client.query("BEGIN");193194// Create order195const orderResult = await client.query(196"INSERT INTO orders (user_id, total) VALUES ($1, $2) RETURNING id",197[userId, calculateTotal(items)],198);199const orderId = orderResult.rows[0].id;200201// Create order items202for (const item of items) {203await client.query(204"INSERT INTO order_items (order_id, product_id, quantity, price) VALUES ($1, $2, $3, $4)",205[orderId, item.productId, item.quantity, item.price],206);207208// Update inventory209await client.query(210"UPDATE products SET stock = stock - $1 WHERE id = $2",211[item.quantity, item.productId],212);213}214215await client.query("COMMIT");216return orderId;217} catch (error) {218await client.query("ROLLBACK");219throw error;220} finally {221client.release();222}223}224}225```226227## Authentication & Authorization228229### JWT Authentication230231```typescript232// services/auth.service.ts233import jwt from "jsonwebtoken";234import bcrypt from "bcrypt";235import { UserRepository } from "../repositories/user.repository";236import { UnauthorizedError } from "../utils/errors";237238export class AuthService {239constructor(private userRepository: UserRepository) {}240241async login(email: string, password: string) {242const user = await this.userRepository.findByEmail(email);243244if (!user) {245throw new UnauthorizedError("Invalid credentials");246}247248const isValid = await bcrypt.compare(password, user.password);249250if (!isValid) {251throw new UnauthorizedError("Invalid credentials");252}253254const token = this.generateToken({255userId: user.id,256email: user.email,257});258259const refreshToken = this.generateRefreshToken({260userId: user.id,261});262263return {264token,265refreshToken,266user: {267id: user.id,268name: user.name,269email: user.email,270},271};272}273274async refreshToken(refreshToken: string) {275try {276const payload = jwt.verify(277refreshToken,278process.env.REFRESH_TOKEN_SECRET!,279) as { userId: string };280281const user = await this.userRepository.findById(payload.userId);282283if (!user) {284throw new UnauthorizedError("User not found");285}286287const token = this.generateToken({288userId: user.id,289email: user.email,290});291292return { token };293} catch (error) {294throw new UnauthorizedError("Invalid refresh token");295}296}297298private generateToken(payload: any): string {299return jwt.sign(payload, process.env.JWT_SECRET!, {300expiresIn: "15m",301});302}303304private generateRefreshToken(payload: any): string {305return jwt.sign(payload, process.env.REFRESH_TOKEN_SECRET!, {306expiresIn: "7d",307});308}309}310```311312## Caching Strategies313314```typescript315// utils/cache.ts316import Redis from "ioredis";317318const redis = new Redis({319host: process.env.REDIS_HOST,320port: parseInt(process.env.REDIS_PORT || "6379"),321retryStrategy: (times) => {322const delay = Math.min(times * 50, 2000);323return delay;324},325});326327export class CacheService {328async get<T>(key: string): Promise<T | null> {329const data = await redis.get(key);330return data ? JSON.parse(data) : null;331}332333async set(key: string, value: any, ttl?: number): Promise<void> {334const serialized = JSON.stringify(value);335if (ttl) {336await redis.setex(key, ttl, serialized);337} else {338await redis.set(key, serialized);339}340}341342async delete(key: string): Promise<void> {343await redis.del(key);344}345346async invalidatePattern(pattern: string): Promise<void> {347const keys = await redis.keys(pattern);348if (keys.length > 0) {349await redis.del(...keys);350}351}352}353354// Cache decorator355export function Cacheable(ttl: number = 300) {356return function (357target: any,358propertyKey: string,359descriptor: PropertyDescriptor,360) {361const originalMethod = descriptor.value;362363descriptor.value = async function (...args: any[]) {364const cache = new CacheService();365const cacheKey = `${propertyKey}:${JSON.stringify(args)}`;366367const cached = await cache.get(cacheKey);368if (cached) {369return cached;370}371372const result = await originalMethod.apply(this, args);373await cache.set(cacheKey, result, ttl);374375return result;376};377378return descriptor;379};380}381```382383## API Response Format384385```typescript386// utils/response.ts387import { Response } from "express";388389export class ApiResponse {390static success<T>(391res: Response,392data: T,393message?: string,394statusCode = 200,395) {396return res.status(statusCode).json({397status: "success",398message,399data,400});401}402403static error(res: Response, message: string, statusCode = 500, errors?: any) {404return res.status(statusCode).json({405status: "error",406message,407...(errors && { errors }),408});409}410411static paginated<T>(412res: Response,413data: T[],414page: number,415limit: number,416total: number,417) {418return res.json({419status: "success",420data,421pagination: {422page,423limit,424total,425pages: Math.ceil(total / limit),426},427});428}429}430```431