[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M70NTMSMY5A447CJ1FN5QXVS":3,"skill-same:01M70NTMSMY5A447CJ1FN5QXVS":78},{"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":61,"ownerDescription":62,"updatedAt":63,"install":64,"resources":77},"01M70NTMSMY5A447CJ1FN5QXVS","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.","shengdabai\u002FTony-Claude-Code-Skills","shengdabai","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F116417419?v=4","01M70F6D2WY5PQV5A92BKYJQEW","Tony Sheng","01M70F6D3GAAAYZ5H4CC1G8VXM",3,"MIT","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fwshobson-agents\u002Fplugins\u002Fjavascript-typescript\u002Fskills\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],"patterns","backend patterns","backend","nodejs","nodejs backend","nodejs backend patterns","node","api","express","when","database","production-ready","design","implementing","middleware","authentication","fastify","backend services","ready","error","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","7850740bd837adb32f0a109ab2af82482196c6f2","skills\u002Fwshobson-agents\u002Fplugins\u002Fjavascript-typescript\u002Fskills\u002Fnodejs-backend-patterns",1,"https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills","https:\u002F\u002Fzturnsgo.com","https:\u002F\u002Fgithub.com\u002Fshengdabai","https:\u002F\u002Fzturnsgo.com\u002F","AI × education creator — producer, not consumer. Taught 6,000+ learners; shipping learning tools & daily essays with Claude Code, practicing @xiaolai's way 🥋","2026-11-01 21:28:23",{"source":8,"skillName":5,"commands":65,"prompt":75,"zipAvailable":76},[66,69,72],{"tool":67,"command":68},"npx","npx skills add shengdabai\u002FTony-Claude-Code-Skills --skill nodejs-backend-patterns",{"tool":70,"command":71},"bunx","bunx skills add shengdabai\u002FTony-Claude-Code-Skills --skill nodejs-backend-patterns",{"tool":73,"command":74},"pnpm","pnpm dlx skills add shengdabai\u002FTony-Claude-Code-Skills --skill nodejs-backend-patterns","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fwshobson-agents\u002Fplugins\u002Fjavascript-typescript\u002Fskills\u002Fnodejs-backend-patterns\u002FSKILL.md",true,[],[79,96,126,152,174,194],{"skillNo":80,"name":81,"nameZh":6,"description":82,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":83,"categories":84,"keywords":88,"publishedAt":49},"01M70P17DAD4C1C47NPH6A96EM","xiaolai-methodology","以李笑来(Li Xiaolai)的思考方式、方法论与表达风格,为用户做全方位 1-on-1 教练指导,覆盖创业、赚钱、投资、学习、自学、读书写作、用 AI、以及人生心态与自我管理。当用户提到 \"李笑来\"、\"笑来\"、\"笑来老师\"、\"扮演李笑来\"、\"找笑来聊聊\"、\"笑来怎么看\"、\"xiaolai\"、\"把时间当作朋友\"、\"通往财富自由之路\"、\"定投改变命运\"、\"韭菜的自我修养\"、\"自学是门手艺\"、\"让时间陪你慢慢变富\"、\"七年就是一辈子\"、\"元认知\"、\"活在未来\"、\"复利\u002F长期主义\"、\"践行\"、\"痴迷改进\"、\"个人商业模式\u002F出售时间\"、\"睡后收入\"、\"定投\"、\"财富自由\"、\"操作系统升级\"、\"最少必要知识\"、\"北极星提示词\"、\"用 AI 该怎么学\u002F思考\" 等任一触发词,或当用户带着关于创业\u002F赚钱\u002F投资\u002F学习\u002F职业\u002F重大人生决策的困惑、希望被人用笑来式的「逻辑 + 大白话 + 类比 + 重新定义 + 践行导向」来点醒和指导时,自动进入李笑来教练模式。","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fxiaolai-methodology\u002FSKILL.md",[85],{"categoryNo":86,"categoryName":87,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000018","写作与文案",[89,90,91,92,93,94,95],"methodology","xiaolai","xiaolai methodology","朋友","投资","writing","收入",{"skillNo":97,"name":98,"nameZh":6,"description":99,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":100,"categories":101,"keywords":106,"publishedAt":49},"01M70P13NB9YP7ACJR1S861CDS","zread","Produce and consume a wiki-style knowledge base for a code repository via the `zread` CLI and its on-disk output under `.\u002F.zread\u002Fwiki\u002F`. Use this skill whenever the user wants to understand, onboard onto, explore, summarize, map, or get an overview of an unfamiliar codebase; asks for architecture docs, a project wiki, a repo walkthrough, module\u002Fpackage explanations, or \"what does this repo do\"; wants to generate, regenerate, resume, browse, or serve code documentation locally; or mentions zread \u002F zread.ai directly. Also use it proactively before diving into a large unknown repo — if `.\u002F.zread\u002Fwiki\u002Fcurrent` exists, read the generated pages instead of crawling source file-by-file; if it doesn't, consider offering to run `zread generate`. The trigger is the intent (understand a codebase through generated docs), not the literal word \"zread\".","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fzread\u002FSKILL.md",[102,105],{"categoryNo":103,"categoryName":104,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},[98,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125],"knowledge","wiki","explore","walkthrough","project","get","knowledge base","run","generate","intent","cli","map","documentation","project wiki","disk","architecture","code","unknown","generated",{"skillNo":127,"name":128,"nameZh":6,"description":129,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":130,"categories":131,"keywords":136,"publishedAt":49},"01M70P10S4SWMZQ8BJAFBFDRW3","youtube","Launch the YouTube Creator Agent to auto-generate videos.\nUse when: user types \u002Fyoutube, wants to create YouTube videos,\ngenerate video content, or upload to YouTube.\nKeywords: youtube, video, 视频, 创作视频, 发布视频","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fyoutube\u002FSKILL.md",[132,133],{"categoryNo":103,"categoryName":104,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":134,"categoryName":135,"taxonomyType":26,"taxonomyTypeDesc":27},"01JBSEEDCAT000000000000036","内容创作者",[128,137,138,139,140,141,142,143,115,144,145,146,38,147,148,149,150,151],"user","content","视频","creator","generate video","videos","launch","video content","create","upload","use","auto","types","Video","创作",{"skillNo":153,"name":154,"nameZh":6,"description":155,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":156,"categories":157,"keywords":159,"publishedAt":49},"01M70P0YE95D8BACZCGV8BK90K","youtube-script-sync","Sync, update, or optimize Tony's GetNote knowledge base `Youtube视频逐字稿` by turning each parent recording or first-person idea note into an idempotent child note containing a directly recordable YouTube long-video or Shorts script. Use when the user says “更新\u002F同步\u002F优化 Youtube视频逐字稿知识库”, or says “更新\u002F同步\u002F优化该知识库” while this knowledge base is the active context.","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fyoutube-script-sync\u002FSKILL.md",[158],{"categoryNo":103,"categoryName":104,"taxonomyType":21,"taxonomyTypeDesc":22},[160,161,128,162,163,150,164,165,166,167,168,169,170,171,113,172,38,147,107,173],"sync","script","youtube script sync","recording","shorts","first","base","person","update","active","child","optimize","idempotent","long",{"skillNo":175,"name":176,"nameZh":6,"description":177,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":178,"categories":179,"keywords":184,"publishedAt":49},"01M70P0WMV5XT0EEC0N445PW07","writing-skills","How to write SKILL.md files that trigger reliably and teach effectively. Use when creating, improving, or reviewing Claude Code skills.","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fxiaolai\u002Fnlpm\u002Fskills\u002Fnlpm\u002Fwriting-skills\u002FSKILL.md",[180,181,182,183],{"categoryNo":86,"categoryName":87,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":103,"categoryName":104,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},{"categoryNo":134,"categoryName":135,"taxonomyType":26,"taxonomyTypeDesc":27},[185,94,186,187,188,189,123,38,147,190,191,192,193],"writing skills","how","teach","write skill","write","claude code skills","files","how to","trigger",{"skillNo":195,"name":196,"nameZh":6,"description":197,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":198,"categories":199,"keywords":202,"publishedAt":49},"01M70P0SJFR4YVJQBGMHKGSKXT","writing-rules","How to write .claude\u002Frules\u002F files that Claude actually follows. Use when creating, improving, or reviewing project rules.","https:\u002F\u002Fgithub.com\u002Fshengdabai\u002FTony-Claude-Code-Skills\u002Fblob\u002Fmain\u002Fskills\u002Fxiaolai\u002Fnlpm\u002Fskills\u002Fnlpm\u002Fwriting-rules\u002FSKILL.md",[200,201],{"categoryNo":86,"categoryName":87,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":134,"categoryName":135,"taxonomyType":26,"taxonomyTypeDesc":27},[203,94,204,111,205,191,38,186,189,147,192],"Rules","writing rules","project rules"]