[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6F1H5J5CTS5JJNETBDJA94C":3,"skill-same:01M6F1H5J5CTS5JJNETBDJA94C":83},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":28,"publishedAt":49,"linkOnly":50,"markdownContent":51,"markdownBody":52,"frontMatter":53,"refName":54,"commitSha":55,"skillDirectory":56,"forksCount":57,"repositoryHtmlUrl":58,"repositoryHomepageUrl":59,"ownerHtmlUrl":60,"ownerBlogUrl":6,"ownerDescription":61,"updatedAt":62,"install":63,"resources":76},"01M6F1H5J5CTS5JJNETBDJA94C","nodejs-backend-patterns",null,"Build production-ready Node.js backend services with Express\u002FFastify, 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.","ComeOnOliver\u002Fskillshub","ComeOnOliver","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F43215937?v=4","01M6E5JNFECJAAVFE0W7SMWWD2","Haoran Shu","01M6E5JNNDQS7AJZDYFQ8TPZCK",66,"MIT","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Faiskillstore\u002Fmarketplace\u002Fwshobson\u002Fnodejs-backend-patterns\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48],"nodejs","nodejs backend","nodejs backend patterns","backend patterns","backend","patterns","middleware","design","fastify","error","architectures","express","backend services","ready","use","node","api","database","best practices","authentication","2026-11-10 10:12:30",false,"---\nname: nodejs-backend-patterns\ndescription: Build production-ready Node.js backend services with Express\u002FFastify, 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.\n---\n\n# Node.js Backend Patterns\n\nComprehensive guidance for building scalable, maintainable, and production-ready Node.js backend applications with modern frameworks, architectural patterns, and best practices.\n\n## When to Use This Skill\n\n- Building REST APIs or GraphQL servers\n- Creating microservices with Node.js\n- Implementing authentication and authorization\n- Designing scalable backend architectures\n- Setting up middleware and error handling\n- Integrating databases (SQL and NoSQL)\n- Building real-time applications with WebSockets\n- Implementing background job processing\n\n## Core Frameworks\n\n### Express.js - Minimalist Framework\n\n**Basic Setup:**\n```typescript\nimport express, { Request, Response, NextFunction } from 'express';\nimport helmet from 'helmet';\nimport cors from 'cors';\nimport compression from 'compression';\n\nconst app = express();\n\n\u002F\u002F Security middleware\napp.use(helmet());\napp.use(cors({ origin: process.env.ALLOWED_ORIGINS?.split(',') }));\napp.use(compression());\n\n\u002F\u002F Body parsing\napp.use(express.json({ limit: '10mb' }));\napp.use(express.urlencoded({ extended: true, limit: '10mb' }));\n\n\u002F\u002F Request logging\napp.use((req: Request, res: Response, next: NextFunction) => {\n  console.log(`${req.method} ${req.path}`);\n  next();\n});\n\nconst PORT = process.env.PORT || 3000;\napp.listen(PORT, () => {\n  console.log(`Server running on port ${PORT}`);\n});\n```\n\n### Fastify - High Performance Framework\n\n**Basic Setup:**\n```typescript\nimport Fastify from 'fastify';\nimport helmet from '@fastify\u002Fhelmet';\nimport cors from '@fastify\u002Fcors';\nimport compress from '@fastify\u002Fcompress';\n\nconst fastify = Fastify({\n  logger: {\n    level: process.env.LOG_LEVEL || 'info',\n    transport: {\n      target: 'pino-pretty',\n      options: { colorize: true }\n    }\n  }\n});\n\n\u002F\u002F Plugins\nawait fastify.register(helmet);\nawait fastify.register(cors, { origin: true });\nawait fastify.register(compress);\n\n\u002F\u002F Type-safe routes with schema validation\nfastify.post\u003C{\n  Body: { name: string; email: string };\n  Reply: { id: string; name: string };\n}>('\u002Fusers', {\n  schema: {\n    body: {\n      type: 'object',\n      required: ['name', 'email'],\n      properties: {\n        name: { type: 'string', minLength: 1 },\n        email: { type: 'string', format: 'email' }\n      }\n    }\n  }\n}, async (request, reply) => {\n  const { name, email } = request.body;\n  return { id: '123', name };\n});\n\nawait fastify.listen({ port: 3000, host: '0.0.0.0' });\n```\n\n## Architectural Patterns\n\n### Pattern 1: Layered Architecture\n\n**Structure:**\n```\nsrc\u002F\n├── controllers\u002F     # Handle HTTP requests\u002Fresponses\n├── services\u002F        # Business logic\n├── repositories\u002F    # Data access layer\n├── models\u002F          # Data models\n├── middleware\u002F      # Express\u002FFastify middleware\n├── routes\u002F          # Route definitions\n├── utils\u002F           # Helper functions\n├── config\u002F          # Configuration\n└── types\u002F           # TypeScript types\n```\n\n**Controller Layer:**\n```typescript\n\u002F\u002F controllers\u002Fuser.controller.ts\nimport { Request, Response, NextFunction } from 'express';\nimport { UserService } from '..\u002Fservices\u002Fuser.service';\nimport { CreateUserDTO, UpdateUserDTO } from '..\u002Ftypes\u002Fuser.types';\n\nexport class UserController {\n  constructor(private userService: UserService) {}\n\n  async createUser(req: Request, res: Response, next: NextFunction) {\n    try {\n      const userData: CreateUserDTO = req.body;\n      const user = await this.userService.createUser(userData);\n      res.status(201).json(user);\n    } catch (error) {\n      next(error);\n    }\n  }\n\n  async getUser(req: Request, res: Response, next: NextFunction) {\n    try {\n      const { id } = req.params;\n      const user = await this.userService.getUserById(id);\n      res.json(user);\n    } catch (error) {\n      next(error);\n    }\n  }\n\n  async updateUser(req: Request, res: Response, next: NextFunction) {\n    try {\n      const { id } = req.params;\n      const updates: UpdateUserDTO = req.body;\n      const user = await this.userService.updateUser(id, updates);\n      res.json(user);\n    } catch (error) {\n      next(error);\n    }\n  }\n\n  async deleteUser(req: Request, res: Response, next: NextFunction) {\n    try {\n      const { id } = req.params;\n      await this.userService.deleteUser(id);\n      res.status(204).send();\n    } catch (error) {\n      next(error);\n    }\n  }\n}\n```\n\n**Service Layer:**\n```typescript\n\u002F\u002F services\u002Fuser.service.ts\nimport { UserRepository } from '..\u002Frepositories\u002Fuser.repository';\nimport { CreateUserDTO, UpdateUserDTO, User } from '..\u002Ftypes\u002Fuser.types';\nimport { NotFoundError, ValidationError } from '..\u002Futils\u002Ferrors';\nimport bcrypt from 'bcrypt';\n\nexport class UserService {\n  constructor(private userRepository: UserRepository) {}\n\n  async createUser(userData: CreateUserDTO): Promise\u003CUser> {\n    \u002F\u002F Validation\n    const existingUser = await this.userRepository.findByEmail(userData.email);\n    if (existingUser) {\n      throw new ValidationError('Email already exists');\n    }\n\n    \u002F\u002F Hash password\n    const hashedPassword = await bcrypt.hash(userData.password, 10);\n\n    \u002F\u002F Create user\n    const user = await this.userRepository.create({\n      ...userData,\n      password: hashedPassword\n    });\n\n    \u002F\u002F Remove password from response\n    const { password, ...userWithoutPassword } = user;\n    return userWithoutPassword as User;\n  }\n\n  async getUserById(id: string): Promise\u003CUser> {\n    const user = await this.userRepository.findById(id);\n    if (!user) {\n      throw new NotFoundError('User not found');\n    }\n    const { password, ...userWithoutPassword } = user;\n    return userWithoutPassword as User;\n  }\n\n  async updateUser(id: string, updates: UpdateUserDTO): Promise\u003CUser> {\n    const user = await this.userRepository.update(id, updates);\n    if (!user) {\n      throw new NotFoundError('User not found');\n    }\n    const { password, ...userWithoutPassword } = user;\n    return userWithoutPassword as User;\n  }\n\n  async deleteUser(id: string): Promise\u003Cvoid> {\n    const deleted = await this.userRepository.delete(id);\n    if (!deleted) {\n      throw new NotFoundError('User not found');\n    }\n  }\n}\n```\n\n**Repository Layer:**\n```typescript\n\u002F\u002F repositories\u002Fuser.repository.ts\nimport { Pool } from 'pg';\nimport { CreateUserDTO, UpdateUserDTO, UserEntity } from '..\u002Ftypes\u002Fuser.types';\n\nexport class UserRepository {\n  constructor(private db: Pool) {}\n\n  async create(userData: CreateUserDTO & { password: string }): Promise\u003CUserEntity> {\n    const query = `\n      INSERT INTO users (name, email, password)\n      VALUES ($1, $2, $3)\n      RETURNING id, name, email, password, created_at, updated_at\n    `;\n    const { rows } = await this.db.query(query, [\n      userData.name,\n      userData.email,\n      userData.password\n    ]);\n    return rows[0];\n  }\n\n  async findById(id: string): Promise\u003CUserEntity | null> {\n    const query = 'SELECT * FROM users WHERE id = $1';\n    const { rows } = await this.db.query(query, [id]);\n    return rows[0] || null;\n  }\n\n  async findByEmail(email: string): Promise\u003CUserEntity | null> {\n    const query = 'SELECT * FROM users WHERE email = $1';\n    const { rows } = await this.db.query(query, [email]);\n    return rows[0] || null;\n  }\n\n  async update(id: string, updates: UpdateUserDTO): Promise\u003CUserEntity | null> {\n    const fields = Object.keys(updates);\n    const values = Object.values(updates);\n\n    const setClause = fields\n      .map((field, idx) => `${field} = $${idx + 2}`)\n      .join(', ');\n\n    const query = `\n      UPDATE users\n      SET ${setClause}, updated_at = CURRENT_TIMESTAMP\n      WHERE id = $1\n      RETURNING *\n    `;\n\n    const { rows } = await this.db.query(query, [id, ...values]);\n    return rows[0] || null;\n  }\n\n  async delete(id: string): Promise\u003Cboolean> {\n    const query = 'DELETE FROM users WHERE id = $1';\n    const { rowCount } = await this.db.query(query, [id]);\n    return rowCount > 0;\n  }\n}\n```\n\n### Pattern 2: Dependency Injection\n\n**DI Container:**\n```typescript\n\u002F\u002F di-container.ts\nimport { Pool } from 'pg';\nimport { UserRepository } from '.\u002Frepositories\u002Fuser.repository';\nimport { UserService } from '.\u002Fservices\u002Fuser.service';\nimport { UserController } from '.\u002Fcontrollers\u002Fuser.controller';\nimport { AuthService } from '.\u002Fservices\u002Fauth.service';\n\nclass Container {\n  private instances = new Map\u003Cstring, any>();\n\n  register\u003CT>(key: string, factory: () => T): void {\n    this.instances.set(key, factory);\n  }\n\n  resolve\u003CT>(key: string): T {\n    const factory = this.instances.get(key);\n    if (!factory) {\n      throw new Error(`No factory registered for ${key}`);\n    }\n    return factory();\n  }\n\n  singleton\u003CT>(key: string, factory: () => T): void {\n    let instance: T;\n    this.instances.set(key, () => {\n      if (!instance) {\n        instance = factory();\n      }\n      return instance;\n    });\n  }\n}\n\nexport const container = new Container();\n\n\u002F\u002F Register dependencies\ncontainer.singleton('db', () => new Pool({\n  host: process.env.DB_HOST,\n  port: parseInt(process.env.DB_PORT || '5432'),\n  database: process.env.DB_NAME,\n  user: process.env.DB_USER,\n  password: process.env.DB_PASSWORD,\n  max: 20,\n  idleTimeoutMillis: 30000,\n  connectionTimeoutMillis: 2000,\n}));\n\ncontainer.singleton('userRepository', () =>\n  new UserRepository(container.resolve('db'))\n);\n\ncontainer.singleton('userService', () =>\n  new UserService(container.resolve('userRepository'))\n);\n\ncontainer.register('userController', () =>\n  new UserController(container.resolve('userService'))\n);\n\ncontainer.singleton('authService', () =>\n  new AuthService(container.resolve('userRepository'))\n);\n```\n\n## Middleware Patterns\n\n### Authentication Middleware\n\n```typescript\n\u002F\u002F middleware\u002Fauth.middleware.ts\nimport { Request, Response, NextFunction } from 'express';\nimport jwt from 'jsonwebtoken';\nimport { UnauthorizedError } from '..\u002Futils\u002Ferrors';\n\ninterface JWTPayload {\n  userId: string;\n  email: string;\n}\n\ndeclare global {\n  namespace Express {\n    interface Request {\n      user?: JWTPayload;\n    }\n  }\n}\n\nexport const authenticate = async (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  try {\n    const token = req.headers.authorization?.replace('Bearer ', '');\n\n    if (!token) {\n      throw new UnauthorizedError('No token provided');\n    }\n\n    const payload = jwt.verify(\n      token,\n      process.env.JWT_SECRET!\n    ) as JWTPayload;\n\n    req.user = payload;\n    next();\n  } catch (error) {\n    next(new UnauthorizedError('Invalid token'));\n  }\n};\n\nexport const authorize = (...roles: string[]) => {\n  return async (req: Request, res: Response, next: NextFunction) => {\n    if (!req.user) {\n      return next(new UnauthorizedError('Not authenticated'));\n    }\n\n    \u002F\u002F Check if user has required role\n    const hasRole = roles.some(role =>\n      req.user?.roles?.includes(role)\n    );\n\n    if (!hasRole) {\n      return next(new UnauthorizedError('Insufficient permissions'));\n    }\n\n    next();\n  };\n};\n```\n\n### Validation Middleware\n\n```typescript\n\u002F\u002F middleware\u002Fvalidation.middleware.ts\nimport { Request, Response, NextFunction } from 'express';\nimport { AnyZodObject, ZodError } from 'zod';\nimport { ValidationError } from '..\u002Futils\u002Ferrors';\n\nexport const validate = (schema: AnyZodObject) => {\n  return async (req: Request, res: Response, next: NextFunction) => {\n    try {\n      await schema.parseAsync({\n        body: req.body,\n        query: req.query,\n        params: req.params\n      });\n      next();\n    } catch (error) {\n      if (error instanceof ZodError) {\n        const errors = error.errors.map(err => ({\n          field: err.path.join('.'),\n          message: err.message\n        }));\n        next(new ValidationError('Validation failed', errors));\n      } else {\n        next(error);\n      }\n    }\n  };\n};\n\n\u002F\u002F Usage with Zod\nimport { z } from 'zod';\n\nconst createUserSchema = z.object({\n  body: z.object({\n    name: z.string().min(1),\n    email: z.string().email(),\n    password: z.string().min(8)\n  })\n});\n\nrouter.post('\u002Fusers', validate(createUserSchema), userController.createUser);\n```\n\n### Rate Limiting Middleware\n\n```typescript\n\u002F\u002F middleware\u002Frate-limit.middleware.ts\nimport rateLimit from 'express-rate-limit';\nimport RedisStore from 'rate-limit-redis';\nimport Redis from 'ioredis';\n\nconst redis = new Redis({\n  host: process.env.REDIS_HOST,\n  port: parseInt(process.env.REDIS_PORT || '6379')\n});\n\nexport const apiLimiter = rateLimit({\n  store: new RedisStore({\n    client: redis,\n    prefix: 'rl:',\n  }),\n  windowMs: 15 * 60 * 1000, \u002F\u002F 15 minutes\n  max: 100, \u002F\u002F Limit each IP to 100 requests per windowMs\n  message: 'Too many requests from this IP, please try again later',\n  standardHeaders: true,\n  legacyHeaders: false,\n});\n\nexport const authLimiter = rateLimit({\n  store: new RedisStore({\n    client: redis,\n    prefix: 'rl:auth:',\n  }),\n  windowMs: 15 * 60 * 1000,\n  max: 5, \u002F\u002F Stricter limit for auth endpoints\n  skipSuccessfulRequests: true,\n});\n```\n\n### Request Logging Middleware\n\n```typescript\n\u002F\u002F middleware\u002Flogger.middleware.ts\nimport { Request, Response, NextFunction } from 'express';\nimport pino from 'pino';\n\nconst logger = pino({\n  level: process.env.LOG_LEVEL || 'info',\n  transport: {\n    target: 'pino-pretty',\n    options: { colorize: true }\n  }\n});\n\nexport const requestLogger = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  const start = Date.now();\n\n  \u002F\u002F Log response when finished\n  res.on('finish', () => {\n    const duration = Date.now() - start;\n    logger.info({\n      method: req.method,\n      url: req.url,\n      status: res.statusCode,\n      duration: `${duration}ms`,\n      userAgent: req.headers['user-agent'],\n      ip: req.ip\n    });\n  });\n\n  next();\n};\n\nexport { logger };\n```\n\n## Error Handling\n\n### Custom Error Classes\n\n```typescript\n\u002F\u002F utils\u002Ferrors.ts\nexport class AppError extends Error {\n  constructor(\n    public message: string,\n    public statusCode: number = 500,\n    public isOperational: boolean = true\n  ) {\n    super(message);\n    Object.setPrototypeOf(this, AppError.prototype);\n    Error.captureStackTrace(this, this.constructor);\n  }\n}\n\nexport class ValidationError extends AppError {\n  constructor(message: string, public errors?: any[]) {\n    super(message, 400);\n  }\n}\n\nexport class NotFoundError extends AppError {\n  constructor(message: string = 'Resource not found') {\n    super(message, 404);\n  }\n}\n\nexport class UnauthorizedError extends AppError {\n  constructor(message: string = 'Unauthorized') {\n    super(message, 401);\n  }\n}\n\nexport class ForbiddenError extends AppError {\n  constructor(message: string = 'Forbidden') {\n    super(message, 403);\n  }\n}\n\nexport class ConflictError extends AppError {\n  constructor(message: string) {\n    super(message, 409);\n  }\n}\n```\n\n### Global Error Handler\n\n```typescript\n\u002F\u002F middleware\u002Ferror-handler.ts\nimport { Request, Response, NextFunction } from 'express';\nimport { AppError } from '..\u002Futils\u002Ferrors';\nimport { logger } from '.\u002Flogger.middleware';\n\nexport const errorHandler = (\n  err: Error,\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  if (err instanceof AppError) {\n    return res.status(err.statusCode).json({\n      status: 'error',\n      message: err.message,\n      ...(err instanceof ValidationError && { errors: err.errors })\n    });\n  }\n\n  \u002F\u002F Log unexpected errors\n  logger.error({\n    error: err.message,\n    stack: err.stack,\n    url: req.url,\n    method: req.method\n  });\n\n  \u002F\u002F Don't leak error details in production\n  const message = process.env.NODE_ENV === 'production'\n    ? 'Internal server error'\n    : err.message;\n\n  res.status(500).json({\n    status: 'error',\n    message\n  });\n};\n\n\u002F\u002F Async error wrapper\nexport const asyncHandler = (\n  fn: (req: Request, res: Response, next: NextFunction) => Promise\u003Cany>\n) => {\n  return (req: Request, res: Response, next: NextFunction) => {\n    Promise.resolve(fn(req, res, next)).catch(next);\n  };\n};\n```\n\n## Database Patterns\n\n### PostgreSQL with Connection Pool\n\n```typescript\n\u002F\u002F config\u002Fdatabase.ts\nimport { Pool, PoolConfig } from 'pg';\n\nconst poolConfig: PoolConfig = {\n  host: process.env.DB_HOST,\n  port: parseInt(process.env.DB_PORT || '5432'),\n  database: process.env.DB_NAME,\n  user: process.env.DB_USER,\n  password: process.env.DB_PASSWORD,\n  max: 20,\n  idleTimeoutMillis: 30000,\n  connectionTimeoutMillis: 2000,\n};\n\nexport const pool = new Pool(poolConfig);\n\n\u002F\u002F Test connection\npool.on('connect', () => {\n  console.log('Database connected');\n});\n\npool.on('error', (err) => {\n  console.error('Unexpected database error', err);\n  process.exit(-1);\n});\n\n\u002F\u002F Graceful shutdown\nexport const closeDatabase = async () => {\n  await pool.end();\n  console.log('Database connection closed');\n};\n```\n\n### MongoDB with Mongoose\n\n```typescript\n\u002F\u002F config\u002Fmongoose.ts\nimport mongoose from 'mongoose';\n\nconst connectDB = async () => {\n  try {\n    await mongoose.connect(process.env.MONGODB_URI!, {\n      maxPoolSize: 10,\n      serverSelectionTimeoutMS: 5000,\n      socketTimeoutMS: 45000,\n    });\n\n    console.log('MongoDB connected');\n  } catch (error) {\n    console.error('MongoDB connection error:', error);\n    process.exit(1);\n  }\n};\n\nmongoose.connection.on('disconnected', () => {\n  console.log('MongoDB disconnected');\n});\n\nmongoose.connection.on('error', (err) => {\n  console.error('MongoDB error:', err);\n});\n\nexport { connectDB };\n\n\u002F\u002F Model example\nimport { Schema, model, Document } from 'mongoose';\n\ninterface IUser extends Document {\n  name: string;\n  email: string;\n  password: string;\n  createdAt: Date;\n  updatedAt: Date;\n}\n\nconst userSchema = new Schema\u003CIUser>({\n  name: { type: String, required: true },\n  email: { type: String, required: true, unique: true },\n  password: { type: String, required: true },\n}, {\n  timestamps: true\n});\n\n\u002F\u002F Indexes\nuserSchema.index({ email: 1 });\n\nexport const User = model\u003CIUser>('User', userSchema);\n```\n\n### Transaction Pattern\n\n```typescript\n\u002F\u002F services\u002Forder.service.ts\nimport { Pool } from 'pg';\n\nexport class OrderService {\n  constructor(private db: Pool) {}\n\n  async createOrder(userId: string, items: any[]) {\n    const client = await this.db.connect();\n\n    try {\n      await client.query('BEGIN');\n\n      \u002F\u002F Create order\n      const orderResult = await client.query(\n        'INSERT INTO orders (user_id, total) VALUES ($1, $2) RETURNING id',\n        [userId, calculateTotal(items)]\n      );\n      const orderId = orderResult.rows[0].id;\n\n      \u002F\u002F Create order items\n      for (const item of items) {\n        await client.query(\n          'INSERT INTO order_items (order_id, product_id, quantity, price) VALUES ($1, $2, $3, $4)',\n          [orderId, item.productId, item.quantity, item.price]\n        );\n\n        \u002F\u002F Update inventory\n        await client.query(\n          'UPDATE products SET stock = stock - $1 WHERE id = $2',\n          [item.quantity, item.productId]\n        );\n      }\n\n      await client.query('COMMIT');\n      return orderId;\n    } catch (error) {\n      await client.query('ROLLBACK');\n      throw error;\n    } finally {\n      client.release();\n    }\n  }\n}\n```\n\n## Authentication & Authorization\n\n### JWT Authentication\n\n```typescript\n\u002F\u002F services\u002Fauth.service.ts\nimport jwt from 'jsonwebtoken';\nimport bcrypt from 'bcrypt';\nimport { UserRepository } from '..\u002Frepositories\u002Fuser.repository';\nimport { UnauthorizedError } from '..\u002Futils\u002Ferrors';\n\nexport class AuthService {\n  constructor(private userRepository: UserRepository) {}\n\n  async login(email: string, password: string) {\n    const user = await this.userRepository.findByEmail(email);\n\n    if (!user) {\n      throw new UnauthorizedError('Invalid credentials');\n    }\n\n    const isValid = await bcrypt.compare(password, user.password);\n\n    if (!isValid) {\n      throw new UnauthorizedError('Invalid credentials');\n    }\n\n    const token = this.generateToken({\n      userId: user.id,\n      email: user.email\n    });\n\n    const refreshToken = this.generateRefreshToken({\n      userId: user.id\n    });\n\n    return {\n      token,\n      refreshToken,\n      user: {\n        id: user.id,\n        name: user.name,\n        email: user.email\n      }\n    };\n  }\n\n  async refreshToken(refreshToken: string) {\n    try {\n      const payload = jwt.verify(\n        refreshToken,\n        process.env.REFRESH_TOKEN_SECRET!\n      ) as { userId: string };\n\n      const user = await this.userRepository.findById(payload.userId);\n\n      if (!user) {\n        throw new UnauthorizedError('User not found');\n      }\n\n      const token = this.generateToken({\n        userId: user.id,\n        email: user.email\n      });\n\n      return { token };\n    } catch (error) {\n      throw new UnauthorizedError('Invalid refresh token');\n    }\n  }\n\n  private generateToken(payload: any): string {\n    return jwt.sign(payload, process.env.JWT_SECRET!, {\n      expiresIn: '15m'\n    });\n  }\n\n  private generateRefreshToken(payload: any): string {\n    return jwt.sign(payload, process.env.REFRESH_TOKEN_SECRET!, {\n      expiresIn: '7d'\n    });\n  }\n}\n```\n\n## Caching Strategies\n\n```typescript\n\u002F\u002F utils\u002Fcache.ts\nimport Redis from 'ioredis';\n\nconst redis = new Redis({\n  host: process.env.REDIS_HOST,\n  port: parseInt(process.env.REDIS_PORT || '6379'),\n  retryStrategy: (times) => {\n    const delay = Math.min(times * 50, 2000);\n    return delay;\n  }\n});\n\nexport class CacheService {\n  async get\u003CT>(key: string): Promise\u003CT | null> {\n    const data = await redis.get(key);\n    return data ? JSON.parse(data) : null;\n  }\n\n  async set(key: string, value: any, ttl?: number): Promise\u003Cvoid> {\n    const serialized = JSON.stringify(value);\n    if (ttl) {\n      await redis.setex(key, ttl, serialized);\n    } else {\n      await redis.set(key, serialized);\n    }\n  }\n\n  async delete(key: string): Promise\u003Cvoid> {\n    await redis.del(key);\n  }\n\n  async invalidatePattern(pattern: string): Promise\u003Cvoid> {\n    const keys = await redis.keys(pattern);\n    if (keys.length > 0) {\n      await redis.del(...keys);\n    }\n  }\n}\n\n\u002F\u002F Cache decorator\nexport function Cacheable(ttl: number = 300) {\n  return function (\n    target: any,\n    propertyKey: string,\n    descriptor: PropertyDescriptor\n  ) {\n    const originalMethod = descriptor.value;\n\n    descriptor.value = async function (...args: any[]) {\n      const cache = new CacheService();\n      const cacheKey = `${propertyKey}:${JSON.stringify(args)}`;\n\n      const cached = await cache.get(cacheKey);\n      if (cached) {\n        return cached;\n      }\n\n      const result = await originalMethod.apply(this, args);\n      await cache.set(cacheKey, result, ttl);\n\n      return result;\n    };\n\n    return descriptor;\n  };\n}\n```\n\n## API Response Format\n\n```typescript\n\u002F\u002F utils\u002Fresponse.ts\nimport { Response } from 'express';\n\nexport class ApiResponse {\n  static success\u003CT>(res: Response, data: T, message?: string, statusCode = 200) {\n    return res.status(statusCode).json({\n      status: 'success',\n      message,\n      data\n    });\n  }\n\n  static error(res: Response, message: string, statusCode = 500, errors?: any) {\n    return res.status(statusCode).json({\n      status: 'error',\n      message,\n      ...(errors && { errors })\n    });\n  }\n\n  static paginated\u003CT>(\n    res: Response,\n    data: T[],\n    page: number,\n    limit: number,\n    total: number\n  ) {\n    return res.json({\n      status: 'success',\n      data,\n      pagination: {\n        page,\n        limit,\n        total,\n        pages: Math.ceil(total \u002F limit)\n      }\n    });\n  }\n}\n```\n\n## Best Practices\n\n1. **Use TypeScript**: Type safety prevents runtime errors\n2. **Implement proper error handling**: Use custom error classes\n3. **Validate input**: Use libraries like Zod or Joi\n4. **Use environment variables**: Never hardcode secrets\n5. **Implement logging**: Use structured logging (Pino, Winston)\n6. **Add rate limiting**: Prevent abuse\n7. **Use HTTPS**: Always in production\n8. **Implement CORS properly**: Don't use `*` in production\n9. **Use dependency injection**: Easier testing and maintenance\n10. **Write tests**: Unit, integration, and E2E tests\n11. **Handle graceful shutdown**: Clean up resources\n12. **Use connection pooling**: For databases\n13. **Implement health checks**: For monitoring\n14. **Use compression**: Reduce response size\n15. **Monitor performance**: Use APM tools\n\n## Testing Patterns\n\nSee `javascript-testing-patterns` skill for comprehensive testing guidance.\n\n## Resources\n\n- **Node.js Best Practices**: https:\u002F\u002Fgithub.com\u002Fgoldbergyoni\u002Fnodebestpractices\n- **Express.js Guide**: https:\u002F\u002Fexpressjs.com\u002Fen\u002Fguide\u002F\n- **Fastify Documentation**: https:\u002F\u002Fwww.fastify.io\u002Fdocs\u002F\n- **TypeScript Node Starter**: https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FTypeScript-Node-Starter\n","# Node.js Backend Patterns\n\nComprehensive guidance for building scalable, maintainable, and production-ready Node.js backend applications with modern frameworks, architectural patterns, and best practices.\n\n## When to Use This Skill\n\n- Building REST APIs or GraphQL servers\n- Creating microservices with Node.js\n- Implementing authentication and authorization\n- Designing scalable backend architectures\n- Setting up middleware and error handling\n- Integrating databases (SQL and NoSQL)\n- Building real-time applications with WebSockets\n- Implementing background job processing\n\n## Core Frameworks\n\n### Express.js - Minimalist Framework\n\n**Basic Setup:**\n```typescript\nimport express, { Request, Response, NextFunction } from 'express';\nimport helmet from 'helmet';\nimport cors from 'cors';\nimport compression from 'compression';\n\nconst app = express();\n\n\u002F\u002F Security middleware\napp.use(helmet());\napp.use(cors({ origin: process.env.ALLOWED_ORIGINS?.split(',') }));\napp.use(compression());\n\n\u002F\u002F Body parsing\napp.use(express.json({ limit: '10mb' }));\napp.use(express.urlencoded({ extended: true, limit: '10mb' }));\n\n\u002F\u002F Request logging\napp.use((req: Request, res: Response, next: NextFunction) => {\n  console.log(`${req.method} ${req.path}`);\n  next();\n});\n\nconst PORT = process.env.PORT || 3000;\napp.listen(PORT, () => {\n  console.log(`Server running on port ${PORT}`);\n});\n```\n\n### Fastify - High Performance Framework\n\n**Basic Setup:**\n```typescript\nimport Fastify from 'fastify';\nimport helmet from '@fastify\u002Fhelmet';\nimport cors from '@fastify\u002Fcors';\nimport compress from '@fastify\u002Fcompress';\n\nconst fastify = Fastify({\n  logger: {\n    level: process.env.LOG_LEVEL || 'info',\n    transport: {\n      target: 'pino-pretty',\n      options: { colorize: true }\n    }\n  }\n});\n\n\u002F\u002F Plugins\nawait fastify.register(helmet);\nawait fastify.register(cors, { origin: true });\nawait fastify.register(compress);\n\n\u002F\u002F Type-safe routes with schema validation\nfastify.post\u003C{\n  Body: { name: string; email: string };\n  Reply: { id: string; name: string };\n}>('\u002Fusers', {\n  schema: {\n    body: {\n      type: 'object',\n      required: ['name', 'email'],\n      properties: {\n        name: { type: 'string', minLength: 1 },\n        email: { type: 'string', format: 'email' }\n      }\n    }\n  }\n}, async (request, reply) => {\n  const { name, email } = request.body;\n  return { id: '123', name };\n});\n\nawait fastify.listen({ port: 3000, host: '0.0.0.0' });\n```\n\n## Architectural Patterns\n\n### Pattern 1: Layered Architecture\n\n**Structure:**\n```\nsrc\u002F\n├── controllers\u002F     # Handle HTTP requests\u002Fresponses\n├── services\u002F        # Business logic\n├── repositories\u002F    # Data access layer\n├── models\u002F          # Data models\n├── middleware\u002F      # Express\u002FFastify middleware\n├── routes\u002F          # Route definitions\n├── utils\u002F           # Helper functions\n├── config\u002F          # Configuration\n└── types\u002F           # TypeScript types\n```\n\n**Controller Layer:**\n```typescript\n\u002F\u002F controllers\u002Fuser.controller.ts\nimport { Request, Response, NextFunction } from 'express';\nimport { UserService } from '..\u002Fservices\u002Fuser.service';\nimport { CreateUserDTO, UpdateUserDTO } from '..\u002Ftypes\u002Fuser.types';\n\nexport class UserController {\n  constructor(private userService: UserService) {}\n\n  async createUser(req: Request, res: Response, next: NextFunction) {\n    try {\n      const userData: CreateUserDTO = req.body;\n      const user = await this.userService.createUser(userData);\n      res.status(201).json(user);\n    } catch (error) {\n      next(error);\n    }\n  }\n\n  async getUser(req: Request, res: Response, next: NextFunction) {\n    try {\n      const { id } = req.params;\n      const user = await this.userService.getUserById(id);\n      res.json(user);\n    } catch (error) {\n      next(error);\n    }\n  }\n\n  async updateUser(req: Request, res: Response, next: NextFunction) {\n    try {\n      const { id } = req.params;\n      const updates: UpdateUserDTO = req.body;\n      const user = await this.userService.updateUser(id, updates);\n      res.json(user);\n    } catch (error) {\n      next(error);\n    }\n  }\n\n  async deleteUser(req: Request, res: Response, next: NextFunction) {\n    try {\n      const { id } = req.params;\n      await this.userService.deleteUser(id);\n      res.status(204).send();\n    } catch (error) {\n      next(error);\n    }\n  }\n}\n```\n\n**Service Layer:**\n```typescript\n\u002F\u002F services\u002Fuser.service.ts\nimport { UserRepository } from '..\u002Frepositories\u002Fuser.repository';\nimport { CreateUserDTO, UpdateUserDTO, User } from '..\u002Ftypes\u002Fuser.types';\nimport { NotFoundError, ValidationError } from '..\u002Futils\u002Ferrors';\nimport bcrypt from 'bcrypt';\n\nexport class UserService {\n  constructor(private userRepository: UserRepository) {}\n\n  async createUser(userData: CreateUserDTO): Promise\u003CUser> {\n    \u002F\u002F Validation\n    const existingUser = await this.userRepository.findByEmail(userData.email);\n    if (existingUser) {\n      throw new ValidationError('Email already exists');\n    }\n\n    \u002F\u002F Hash password\n    const hashedPassword = await bcrypt.hash(userData.password, 10);\n\n    \u002F\u002F Create user\n    const user = await this.userRepository.create({\n      ...userData,\n      password: hashedPassword\n    });\n\n    \u002F\u002F Remove password from response\n    const { password, ...userWithoutPassword } = user;\n    return userWithoutPassword as User;\n  }\n\n  async getUserById(id: string): Promise\u003CUser> {\n    const user = await this.userRepository.findById(id);\n    if (!user) {\n      throw new NotFoundError('User not found');\n    }\n    const { password, ...userWithoutPassword } = user;\n    return userWithoutPassword as User;\n  }\n\n  async updateUser(id: string, updates: UpdateUserDTO): Promise\u003CUser> {\n    const user = await this.userRepository.update(id, updates);\n    if (!user) {\n      throw new NotFoundError('User not found');\n    }\n    const { password, ...userWithoutPassword } = user;\n    return userWithoutPassword as User;\n  }\n\n  async deleteUser(id: string): Promise\u003Cvoid> {\n    const deleted = await this.userRepository.delete(id);\n    if (!deleted) {\n      throw new NotFoundError('User not found');\n    }\n  }\n}\n```\n\n**Repository Layer:**\n```typescript\n\u002F\u002F repositories\u002Fuser.repository.ts\nimport { Pool } from 'pg';\nimport { CreateUserDTO, UpdateUserDTO, UserEntity } from '..\u002Ftypes\u002Fuser.types';\n\nexport class UserRepository {\n  constructor(private db: Pool) {}\n\n  async create(userData: CreateUserDTO & { password: string }): Promise\u003CUserEntity> {\n    const query = `\n      INSERT INTO users (name, email, password)\n      VALUES ($1, $2, $3)\n      RETURNING id, name, email, password, created_at, updated_at\n    `;\n    const { rows } = await this.db.query(query, [\n      userData.name,\n      userData.email,\n      userData.password\n    ]);\n    return rows[0];\n  }\n\n  async findById(id: string): Promise\u003CUserEntity | null> {\n    const query = 'SELECT * FROM users WHERE id = $1';\n    const { rows } = await this.db.query(query, [id]);\n    return rows[0] || null;\n  }\n\n  async findByEmail(email: string): Promise\u003CUserEntity | null> {\n    const query = 'SELECT * FROM users WHERE email = $1';\n    const { rows } = await this.db.query(query, [email]);\n    return rows[0] || null;\n  }\n\n  async update(id: string, updates: UpdateUserDTO): Promise\u003CUserEntity | null> {\n    const fields = Object.keys(updates);\n    const values = Object.values(updates);\n\n    const setClause = fields\n      .map((field, idx) => `${field} = $${idx + 2}`)\n      .join(', ');\n\n    const query = `\n      UPDATE users\n      SET ${setClause}, updated_at = CURRENT_TIMESTAMP\n      WHERE id = $1\n      RETURNING *\n    `;\n\n    const { rows } = await this.db.query(query, [id, ...values]);\n    return rows[0] || null;\n  }\n\n  async delete(id: string): Promise\u003Cboolean> {\n    const query = 'DELETE FROM users WHERE id = $1';\n    const { rowCount } = await this.db.query(query, [id]);\n    return rowCount > 0;\n  }\n}\n```\n\n### Pattern 2: Dependency Injection\n\n**DI Container:**\n```typescript\n\u002F\u002F di-container.ts\nimport { Pool } from 'pg';\nimport { UserRepository } from '.\u002Frepositories\u002Fuser.repository';\nimport { UserService } from '.\u002Fservices\u002Fuser.service';\nimport { UserController } from '.\u002Fcontrollers\u002Fuser.controller';\nimport { AuthService } from '.\u002Fservices\u002Fauth.service';\n\nclass Container {\n  private instances = new Map\u003Cstring, any>();\n\n  register\u003CT>(key: string, factory: () => T): void {\n    this.instances.set(key, factory);\n  }\n\n  resolve\u003CT>(key: string): T {\n    const factory = this.instances.get(key);\n    if (!factory) {\n      throw new Error(`No factory registered for ${key}`);\n    }\n    return factory();\n  }\n\n  singleton\u003CT>(key: string, factory: () => T): void {\n    let instance: T;\n    this.instances.set(key, () => {\n      if (!instance) {\n        instance = factory();\n      }\n      return instance;\n    });\n  }\n}\n\nexport const container = new Container();\n\n\u002F\u002F Register dependencies\ncontainer.singleton('db', () => new Pool({\n  host: process.env.DB_HOST,\n  port: parseInt(process.env.DB_PORT || '5432'),\n  database: process.env.DB_NAME,\n  user: process.env.DB_USER,\n  password: process.env.DB_PASSWORD,\n  max: 20,\n  idleTimeoutMillis: 30000,\n  connectionTimeoutMillis: 2000,\n}));\n\ncontainer.singleton('userRepository', () =>\n  new UserRepository(container.resolve('db'))\n);\n\ncontainer.singleton('userService', () =>\n  new UserService(container.resolve('userRepository'))\n);\n\ncontainer.register('userController', () =>\n  new UserController(container.resolve('userService'))\n);\n\ncontainer.singleton('authService', () =>\n  new AuthService(container.resolve('userRepository'))\n);\n```\n\n## Middleware Patterns\n\n### Authentication Middleware\n\n```typescript\n\u002F\u002F middleware\u002Fauth.middleware.ts\nimport { Request, Response, NextFunction } from 'express';\nimport jwt from 'jsonwebtoken';\nimport { UnauthorizedError } from '..\u002Futils\u002Ferrors';\n\ninterface JWTPayload {\n  userId: string;\n  email: string;\n}\n\ndeclare global {\n  namespace Express {\n    interface Request {\n      user?: JWTPayload;\n    }\n  }\n}\n\nexport const authenticate = async (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  try {\n    const token = req.headers.authorization?.replace('Bearer ', '');\n\n    if (!token) {\n      throw new UnauthorizedError('No token provided');\n    }\n\n    const payload = jwt.verify(\n      token,\n      process.env.JWT_SECRET!\n    ) as JWTPayload;\n\n    req.user = payload;\n    next();\n  } catch (error) {\n    next(new UnauthorizedError('Invalid token'));\n  }\n};\n\nexport const authorize = (...roles: string[]) => {\n  return async (req: Request, res: Response, next: NextFunction) => {\n    if (!req.user) {\n      return next(new UnauthorizedError('Not authenticated'));\n    }\n\n    \u002F\u002F Check if user has required role\n    const hasRole = roles.some(role =>\n      req.user?.roles?.includes(role)\n    );\n\n    if (!hasRole) {\n      return next(new UnauthorizedError('Insufficient permissions'));\n    }\n\n    next();\n  };\n};\n```\n\n### Validation Middleware\n\n```typescript\n\u002F\u002F middleware\u002Fvalidation.middleware.ts\nimport { Request, Response, NextFunction } from 'express';\nimport { AnyZodObject, ZodError } from 'zod';\nimport { ValidationError } from '..\u002Futils\u002Ferrors';\n\nexport const validate = (schema: AnyZodObject) => {\n  return async (req: Request, res: Response, next: NextFunction) => {\n    try {\n      await schema.parseAsync({\n        body: req.body,\n        query: req.query,\n        params: req.params\n      });\n      next();\n    } catch (error) {\n      if (error instanceof ZodError) {\n        const errors = error.errors.map(err => ({\n          field: err.path.join('.'),\n          message: err.message\n        }));\n        next(new ValidationError('Validation failed', errors));\n      } else {\n        next(error);\n      }\n    }\n  };\n};\n\n\u002F\u002F Usage with Zod\nimport { z } from 'zod';\n\nconst createUserSchema = z.object({\n  body: z.object({\n    name: z.string().min(1),\n    email: z.string().email(),\n    password: z.string().min(8)\n  })\n});\n\nrouter.post('\u002Fusers', validate(createUserSchema), userController.createUser);\n```\n\n### Rate Limiting Middleware\n\n```typescript\n\u002F\u002F middleware\u002Frate-limit.middleware.ts\nimport rateLimit from 'express-rate-limit';\nimport RedisStore from 'rate-limit-redis';\nimport Redis from 'ioredis';\n\nconst redis = new Redis({\n  host: process.env.REDIS_HOST,\n  port: parseInt(process.env.REDIS_PORT || '6379')\n});\n\nexport const apiLimiter = rateLimit({\n  store: new RedisStore({\n    client: redis,\n    prefix: 'rl:',\n  }),\n  windowMs: 15 * 60 * 1000, \u002F\u002F 15 minutes\n  max: 100, \u002F\u002F Limit each IP to 100 requests per windowMs\n  message: 'Too many requests from this IP, please try again later',\n  standardHeaders: true,\n  legacyHeaders: false,\n});\n\nexport const authLimiter = rateLimit({\n  store: new RedisStore({\n    client: redis,\n    prefix: 'rl:auth:',\n  }),\n  windowMs: 15 * 60 * 1000,\n  max: 5, \u002F\u002F Stricter limit for auth endpoints\n  skipSuccessfulRequests: true,\n});\n```\n\n### Request Logging Middleware\n\n```typescript\n\u002F\u002F middleware\u002Flogger.middleware.ts\nimport { Request, Response, NextFunction } from 'express';\nimport pino from 'pino';\n\nconst logger = pino({\n  level: process.env.LOG_LEVEL || 'info',\n  transport: {\n    target: 'pino-pretty',\n    options: { colorize: true }\n  }\n});\n\nexport const requestLogger = (\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  const start = Date.now();\n\n  \u002F\u002F Log response when finished\n  res.on('finish', () => {\n    const duration = Date.now() - start;\n    logger.info({\n      method: req.method,\n      url: req.url,\n      status: res.statusCode,\n      duration: `${duration}ms`,\n      userAgent: req.headers['user-agent'],\n      ip: req.ip\n    });\n  });\n\n  next();\n};\n\nexport { logger };\n```\n\n## Error Handling\n\n### Custom Error Classes\n\n```typescript\n\u002F\u002F utils\u002Ferrors.ts\nexport class AppError extends Error {\n  constructor(\n    public message: string,\n    public statusCode: number = 500,\n    public isOperational: boolean = true\n  ) {\n    super(message);\n    Object.setPrototypeOf(this, AppError.prototype);\n    Error.captureStackTrace(this, this.constructor);\n  }\n}\n\nexport class ValidationError extends AppError {\n  constructor(message: string, public errors?: any[]) {\n    super(message, 400);\n  }\n}\n\nexport class NotFoundError extends AppError {\n  constructor(message: string = 'Resource not found') {\n    super(message, 404);\n  }\n}\n\nexport class UnauthorizedError extends AppError {\n  constructor(message: string = 'Unauthorized') {\n    super(message, 401);\n  }\n}\n\nexport class ForbiddenError extends AppError {\n  constructor(message: string = 'Forbidden') {\n    super(message, 403);\n  }\n}\n\nexport class ConflictError extends AppError {\n  constructor(message: string) {\n    super(message, 409);\n  }\n}\n```\n\n### Global Error Handler\n\n```typescript\n\u002F\u002F middleware\u002Ferror-handler.ts\nimport { Request, Response, NextFunction } from 'express';\nimport { AppError } from '..\u002Futils\u002Ferrors';\nimport { logger } from '.\u002Flogger.middleware';\n\nexport const errorHandler = (\n  err: Error,\n  req: Request,\n  res: Response,\n  next: NextFunction\n) => {\n  if (err instanceof AppError) {\n    return res.status(err.statusCode).json({\n      status: 'error',\n      message: err.message,\n      ...(err instanceof ValidationError && { errors: err.errors })\n    });\n  }\n\n  \u002F\u002F Log unexpected errors\n  logger.error({\n    error: err.message,\n    stack: err.stack,\n    url: req.url,\n    method: req.method\n  });\n\n  \u002F\u002F Don't leak error details in production\n  const message = process.env.NODE_ENV === 'production'\n    ? 'Internal server error'\n    : err.message;\n\n  res.status(500).json({\n    status: 'error',\n    message\n  });\n};\n\n\u002F\u002F Async error wrapper\nexport const asyncHandler = (\n  fn: (req: Request, res: Response, next: NextFunction) => Promise\u003Cany>\n) => {\n  return (req: Request, res: Response, next: NextFunction) => {\n    Promise.resolve(fn(req, res, next)).catch(next);\n  };\n};\n```\n\n## Database Patterns\n\n### PostgreSQL with Connection Pool\n\n```typescript\n\u002F\u002F config\u002Fdatabase.ts\nimport { Pool, PoolConfig } from 'pg';\n\nconst poolConfig: PoolConfig = {\n  host: process.env.DB_HOST,\n  port: parseInt(process.env.DB_PORT || '5432'),\n  database: process.env.DB_NAME,\n  user: process.env.DB_USER,\n  password: process.env.DB_PASSWORD,\n  max: 20,\n  idleTimeoutMillis: 30000,\n  connectionTimeoutMillis: 2000,\n};\n\nexport const pool = new Pool(poolConfig);\n\n\u002F\u002F Test connection\npool.on('connect', () => {\n  console.log('Database connected');\n});\n\npool.on('error', (err) => {\n  console.error('Unexpected database error', err);\n  process.exit(-1);\n});\n\n\u002F\u002F Graceful shutdown\nexport const closeDatabase = async () => {\n  await pool.end();\n  console.log('Database connection closed');\n};\n```\n\n### MongoDB with Mongoose\n\n```typescript\n\u002F\u002F config\u002Fmongoose.ts\nimport mongoose from 'mongoose';\n\nconst connectDB = async () => {\n  try {\n    await mongoose.connect(process.env.MONGODB_URI!, {\n      maxPoolSize: 10,\n      serverSelectionTimeoutMS: 5000,\n      socketTimeoutMS: 45000,\n    });\n\n    console.log('MongoDB connected');\n  } catch (error) {\n    console.error('MongoDB connection error:', error);\n    process.exit(1);\n  }\n};\n\nmongoose.connection.on('disconnected', () => {\n  console.log('MongoDB disconnected');\n});\n\nmongoose.connection.on('error', (err) => {\n  console.error('MongoDB error:', err);\n});\n\nexport { connectDB };\n\n\u002F\u002F Model example\nimport { Schema, model, Document } from 'mongoose';\n\ninterface IUser extends Document {\n  name: string;\n  email: string;\n  password: string;\n  createdAt: Date;\n  updatedAt: Date;\n}\n\nconst userSchema = new Schema\u003CIUser>({\n  name: { type: String, required: true },\n  email: { type: String, required: true, unique: true },\n  password: { type: String, required: true },\n}, {\n  timestamps: true\n});\n\n\u002F\u002F Indexes\nuserSchema.index({ email: 1 });\n\nexport const User = model\u003CIUser>('User', userSchema);\n```\n\n### Transaction Pattern\n\n```typescript\n\u002F\u002F services\u002Forder.service.ts\nimport { Pool } from 'pg';\n\nexport class OrderService {\n  constructor(private db: Pool) {}\n\n  async createOrder(userId: string, items: any[]) {\n    const client = await this.db.connect();\n\n    try {\n      await client.query('BEGIN');\n\n      \u002F\u002F Create order\n      const orderResult = await client.query(\n        'INSERT INTO orders (user_id, total) VALUES ($1, $2) RETURNING id',\n        [userId, calculateTotal(items)]\n      );\n      const orderId = orderResult.rows[0].id;\n\n      \u002F\u002F Create order items\n      for (const item of items) {\n        await client.query(\n          'INSERT INTO order_items (order_id, product_id, quantity, price) VALUES ($1, $2, $3, $4)',\n          [orderId, item.productId, item.quantity, item.price]\n        );\n\n        \u002F\u002F Update inventory\n        await client.query(\n          'UPDATE products SET stock = stock - $1 WHERE id = $2',\n          [item.quantity, item.productId]\n        );\n      }\n\n      await client.query('COMMIT');\n      return orderId;\n    } catch (error) {\n      await client.query('ROLLBACK');\n      throw error;\n    } finally {\n      client.release();\n    }\n  }\n}\n```\n\n## Authentication & Authorization\n\n### JWT Authentication\n\n```typescript\n\u002F\u002F services\u002Fauth.service.ts\nimport jwt from 'jsonwebtoken';\nimport bcrypt from 'bcrypt';\nimport { UserRepository } from '..\u002Frepositories\u002Fuser.repository';\nimport { UnauthorizedError } from '..\u002Futils\u002Ferrors';\n\nexport class AuthService {\n  constructor(private userRepository: UserRepository) {}\n\n  async login(email: string, password: string) {\n    const user = await this.userRepository.findByEmail(email);\n\n    if (!user) {\n      throw new UnauthorizedError('Invalid credentials');\n    }\n\n    const isValid = await bcrypt.compare(password, user.password);\n\n    if (!isValid) {\n      throw new UnauthorizedError('Invalid credentials');\n    }\n\n    const token = this.generateToken({\n      userId: user.id,\n      email: user.email\n    });\n\n    const refreshToken = this.generateRefreshToken({\n      userId: user.id\n    });\n\n    return {\n      token,\n      refreshToken,\n      user: {\n        id: user.id,\n        name: user.name,\n        email: user.email\n      }\n    };\n  }\n\n  async refreshToken(refreshToken: string) {\n    try {\n      const payload = jwt.verify(\n        refreshToken,\n        process.env.REFRESH_TOKEN_SECRET!\n      ) as { userId: string };\n\n      const user = await this.userRepository.findById(payload.userId);\n\n      if (!user) {\n        throw new UnauthorizedError('User not found');\n      }\n\n      const token = this.generateToken({\n        userId: user.id,\n        email: user.email\n      });\n\n      return { token };\n    } catch (error) {\n      throw new UnauthorizedError('Invalid refresh token');\n    }\n  }\n\n  private generateToken(payload: any): string {\n    return jwt.sign(payload, process.env.JWT_SECRET!, {\n      expiresIn: '15m'\n    });\n  }\n\n  private generateRefreshToken(payload: any): string {\n    return jwt.sign(payload, process.env.REFRESH_TOKEN_SECRET!, {\n      expiresIn: '7d'\n    });\n  }\n}\n```\n\n## Caching Strategies\n\n```typescript\n\u002F\u002F utils\u002Fcache.ts\nimport Redis from 'ioredis';\n\nconst redis = new Redis({\n  host: process.env.REDIS_HOST,\n  port: parseInt(process.env.REDIS_PORT || '6379'),\n  retryStrategy: (times) => {\n    const delay = Math.min(times * 50, 2000);\n    return delay;\n  }\n});\n\nexport class CacheService {\n  async get\u003CT>(key: string): Promise\u003CT | null> {\n    const data = await redis.get(key);\n    return data ? JSON.parse(data) : null;\n  }\n\n  async set(key: string, value: any, ttl?: number): Promise\u003Cvoid> {\n    const serialized = JSON.stringify(value);\n    if (ttl) {\n      await redis.setex(key, ttl, serialized);\n    } else {\n      await redis.set(key, serialized);\n    }\n  }\n\n  async delete(key: string): Promise\u003Cvoid> {\n    await redis.del(key);\n  }\n\n  async invalidatePattern(pattern: string): Promise\u003Cvoid> {\n    const keys = await redis.keys(pattern);\n    if (keys.length > 0) {\n      await redis.del(...keys);\n    }\n  }\n}\n\n\u002F\u002F Cache decorator\nexport function Cacheable(ttl: number = 300) {\n  return function (\n    target: any,\n    propertyKey: string,\n    descriptor: PropertyDescriptor\n  ) {\n    const originalMethod = descriptor.value;\n\n    descriptor.value = async function (...args: any[]) {\n      const cache = new CacheService();\n      const cacheKey = `${propertyKey}:${JSON.stringify(args)}`;\n\n      const cached = await cache.get(cacheKey);\n      if (cached) {\n        return cached;\n      }\n\n      const result = await originalMethod.apply(this, args);\n      await cache.set(cacheKey, result, ttl);\n\n      return result;\n    };\n\n    return descriptor;\n  };\n}\n```\n\n## API Response Format\n\n```typescript\n\u002F\u002F utils\u002Fresponse.ts\nimport { Response } from 'express';\n\nexport class ApiResponse {\n  static success\u003CT>(res: Response, data: T, message?: string, statusCode = 200) {\n    return res.status(statusCode).json({\n      status: 'success',\n      message,\n      data\n    });\n  }\n\n  static error(res: Response, message: string, statusCode = 500, errors?: any) {\n    return res.status(statusCode).json({\n      status: 'error',\n      message,\n      ...(errors && { errors })\n    });\n  }\n\n  static paginated\u003CT>(\n    res: Response,\n    data: T[],\n    page: number,\n    limit: number,\n    total: number\n  ) {\n    return res.json({\n      status: 'success',\n      data,\n      pagination: {\n        page,\n        limit,\n        total,\n        pages: Math.ceil(total \u002F limit)\n      }\n    });\n  }\n}\n```\n\n## Best Practices\n\n1. **Use TypeScript**: Type safety prevents runtime errors\n2. **Implement proper error handling**: Use custom error classes\n3. **Validate input**: Use libraries like Zod or Joi\n4. **Use environment variables**: Never hardcode secrets\n5. **Implement logging**: Use structured logging (Pino, Winston)\n6. **Add rate limiting**: Prevent abuse\n7. **Use HTTPS**: Always in production\n8. **Implement CORS properly**: Don't use `*` in production\n9. **Use dependency injection**: Easier testing and maintenance\n10. **Write tests**: Unit, integration, and E2E tests\n11. **Handle graceful shutdown**: Clean up resources\n12. **Use connection pooling**: For databases\n13. **Implement health checks**: For monitoring\n14. **Use compression**: Reduce response size\n15. **Monitor performance**: Use APM tools\n\n## Testing Patterns\n\nSee `javascript-testing-patterns` skill for comprehensive testing guidance.\n\n## Resources\n\n- **Node.js Best Practices**: https:\u002F\u002Fgithub.com\u002Fgoldbergyoni\u002Fnodebestpractices\n- **Express.js Guide**: https:\u002F\u002Fexpressjs.com\u002Fen\u002Fguide\u002F\n- **Fastify Documentation**: https:\u002F\u002Fwww.fastify.io\u002Fdocs\u002F\n- **TypeScript Node Starter**: https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002FTypeScript-Node-Starter\n",{"name":5,"description":7},"main","def8531e65114c0fca8fb8551c1871ee0eed705c","skills\u002Faiskillstore\u002Fmarketplace\u002Fwshobson\u002Fnodejs-backend-patterns",24,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub","https:\u002F\u002Fskillshub.wtf","https:\u002F\u002Fgithub.com\u002FComeOnOliver","AI & Blockchain Engineer\r\n","2026-06-24 22:07:19",{"source":8,"skillName":5,"commands":64,"prompt":74,"zipAvailable":75},[65,68,71],{"tool":66,"command":67},"npx","npx skills add ComeOnOliver\u002Fskillshub --skill nodejs-backend-patterns",{"tool":69,"command":70},"bunx","bunx skills add ComeOnOliver\u002Fskillshub --skill nodejs-backend-patterns",{"tool":72,"command":73},"pnpm","pnpm dlx skills add ComeOnOliver\u002Fskillshub --skill nodejs-backend-patterns","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Faiskillstore\u002Fmarketplace\u002Fwshobson\u002Fnodejs-backend-patterns\u002FSKILL.md",true,[77],{"path":78,"resourceType":79,"resourceTypeDesc":80,"sizeBytes":81,"sourceHtmlUrl":82},"skill-report.json","REFERENCE","参考文档",22512,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Faiskillstore\u002Fmarketplace\u002Fwshobson\u002Fnodejs-backend-patterns\u002Fskill-report.json",[84,96,106,115,121,128],{"skillNo":85,"name":86,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":87,"categories":88,"keywords":92,"publishedAt":49},"01M6FKJN13GMG4KY57J3QD8GY5","model-hierarchy-skill","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzscole\u002Fmodel-hierarchy-skill\u002Fmodel-hierarchy-skill\u002FSKILL.md",[89],{"categoryNo":90,"categoryName":91,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[93,94,95],"model","model hierarchy","model hierarchy skill",{"skillNo":97,"name":98,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":99,"categories":100,"keywords":102,"publishedAt":49},"01M6FKJJJ6GCAMR4PPPRD12M5X","zoom-mcp","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-mcp\u002FSKILL.md",[101],{"categoryNo":90,"categoryName":91,"taxonomyType":21,"taxonomyTypeDesc":22},[103,104,105],"MCP","zoom","zoom mcp",{"skillNo":107,"name":108,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":109,"categories":110,"keywords":111,"publishedAt":49},"01M6FKJE3KYT3KPEZMEEHYKA6K","zoom-apps-sdk","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-apps-sdk\u002FSKILL.md",[],[112,113,114,104],"sdk","zoom apps sdk","apps",{"skillNo":116,"name":117,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":118,"categories":119,"keywords":120,"publishedAt":49},"01M6FKJ9EDH9Q5VWDF8HAJRSP0","windows","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwindows\u002FSKILL.md",[],[117],{"skillNo":122,"name":123,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":124,"categories":125,"keywords":127,"publishedAt":49},"01M6FKJ666BEEFC3AE9GJ72Q93","whiteboard","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwhiteboard\u002FSKILL.md",[126],{"categoryNo":90,"categoryName":91,"taxonomyType":21,"taxonomyTypeDesc":22},[123],{"skillNo":129,"name":130,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":131,"categories":132,"keywords":133,"publishedAt":49},"01M6FKJ21WFCDGTJQ2Q91T9DVW","websockets","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwebsockets\u002FSKILL.md",[],[130]]