# Nodejs Skill

> This skill should be used when the user asks about Node.js, Express.js, Fastify, Hono, Node.js streams, EventEmitter, Buffer, child processes, worker threads, fs/path/os modules, npm/pnpm/yarn, package.json, Node.js performance, clustering, libuv, the event loop, Node.js HTTP server, middleware patterns, REST API design, WebSockets in Node.js, environment variables, or server-side JavaScript runtime topics. Trigger when the user mentions "node", "nodejs", "express", "fastify", "hono", "npm", "pnpm", "require", "module.exports", "ESM", "commonjs", "node:fs", "node:path", "node:http", "process.env", "event loop", "middleware", "REST API", or building backend services with JavaScript/TypeScript.

- Skill: `sirhamza/nodejs-skill` (Agent Skill)
- Install (CLI): `npx skillmds@latest add sirhamza/nodejs-skill`
- Raw SKILL.md: https://api.skillmd.com/api/skills/sirhamza/nodejs-skill/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: SirHamza (https://skillmd.com/u/sirhamza)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/sirhamza/nodejs-skill

---


# Node.js Expert

## Overview

Advanced expertise in Node.js — from the event loop and core modules to building production REST APIs with Express/Fastify/Hono, streams, worker threads, and modern ESM module patterns.

---

## 1. Event Loop & Async Model

```
   ┌─────────────────────────────┐
   │           timers            │  setTimeout, setInterval
   ├─────────────────────────────┤
   │     pending callbacks       │  I/O callbacks deferred to next loop
   ├─────────────────────────────┤
   │       idle, prepare         │  internal
   ├─────────────────────────────┤
   │           poll              │  fetch I/O events, execute callbacks
   ├─────────────────────────────┤
   │           check             │  setImmediate
   ├─────────────────────────────┤
   │      close callbacks        │  socket.on('close')
   └─────────────────────────────┘
   Between each phase: process.nextTick() → Promise microtasks
```

```ts
// Execution order
console.log('1')                          // sync
process.nextTick(() => console.log('2'))  // nextTick queue
Promise.resolve().then(() => console.log('3')) // microtask queue
setImmediate(() => console.log('4'))      // check phase
setTimeout(() => console.log('5'), 0)    // timers phase
// Output: 1, 2, 3, 4, 5 (approximately)
```

---

## 2. Modules — ESM vs CommonJS

```ts
// ESM (modern — use in new projects)
// package.json: "type": "module"
import { readFile } from 'node:fs/promises'
import express from 'express'
export const add = (a: number, b: number) => a + b
export default class Server { }

// CommonJS (legacy)
const fs = require('fs')
module.exports = { add: (a, b) => a + b }
exports.add = (a, b) => a + b

// Dynamic import (works in both)
const { default: chalk } = await import('chalk')

// __dirname equivalent in ESM
import { fileURLToPath } from 'node:url'
import { dirname, join } from 'node:path'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
```

---

## 3. Core Node.js Modules

### File System
```ts
import { readFile, writeFile, readdir, mkdir, rm, stat, watch } from 'node:fs/promises'
import { createReadStream, createWriteStream } from 'node:fs'

// Read / Write
const content = await readFile('./config.json', 'utf-8')
await writeFile('./output.json', JSON.stringify(data, null, 2))

// Directory operations
await mkdir('./logs', { recursive: true })
const files = await readdir('./src', { recursive: true })
await rm('./tmp', { recursive: true, force: true })

// File info
const info = await stat('./file.txt')
console.log(info.size, info.mtime, info.isDirectory())

// Watch for changes
const watcher = watch('./src', { recursive: true })
for await (const event of watcher) {
  console.log(event.eventType, event.filename)
}
```

### Path
```ts
import { join, resolve, dirname, basename, extname, parse, relative } from 'node:path'

join('/users', 'alice', 'docs')     // '/users/alice/docs'
resolve('./src', '../dist')          // absolute path
basename('/foo/bar/baz.ts')          // 'baz.ts'
extname('index.html')                // '.html'
parse('/home/user/file.txt')
// { root: '/', dir: '/home/user', base: 'file.txt', ext: '.txt', name: 'file' }
```

### HTTP / HTTPS
```ts
import { createServer } from 'node:http'

const server = createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'application/json' })
  res.end(JSON.stringify({ ok: true }))
})
server.listen(3000, () => console.log('Listening on :3000'))
```

### Child Processes
```ts
import { exec, spawn, execFile } from 'node:child_process'
import { promisify } from 'node:util'

const execAsync = promisify(exec)
const { stdout } = await execAsync('git log --oneline -10')

// Streaming output
const child = spawn('npm', ['run', 'build'], { stdio: 'inherit' })
child.on('exit', code => console.log('Exit:', code))
```

### Worker Threads
```ts
import { Worker, isMainThread, parentPort, workerData } from 'node:worker_threads'

if (isMainThread) {
  const worker = new Worker('./worker.js', { workerData: { n: 40 } })
  worker.on('message', result => console.log('Result:', result))
  worker.on('error', err => console.error(err))
} else {
  const result = fibonacci(workerData.n)
  parentPort?.postMessage(result)
}
```

---

## 4. Express.js

```ts
import express, { Request, Response, NextFunction } from 'express'
import { z } from 'zod'

const app = express()

// Middleware
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.use(cors({ origin: process.env.ALLOWED_ORIGIN }))

// Route with typed params/body/query
const createUserSchema = z.object({ name: z.string().min(1), email: z.string().email() })

app.post('/users', async (req: Request, res: Response) => {
  const body = createUserSchema.parse(req.body)
  const user = await db.user.create({ data: body })
  res.status(201).json(user)
})

// Middleware pattern
function authenticate(req: Request, res: Response, next: NextFunction) {
  const token = req.headers.authorization?.replace('Bearer ', '')
  if (!token) return res.status(401).json({ error: 'Unauthorized' })
  try {
    req.user = verifyToken(token)
    next()
  } catch {
    res.status(401).json({ error: 'Invalid token' })
  }
}

// Router
const userRouter = express.Router()
userRouter.use(authenticate)
userRouter.get('/', getUsers)
userRouter.get('/:id', getUser)
userRouter.put('/:id', updateUser)
userRouter.delete('/:id', deleteUser)
app.use('/api/users', userRouter)

// Global error handler (must have 4 params)
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
  console.error(err)
  if (err instanceof z.ZodError) return res.status(400).json({ errors: err.errors })
  res.status(500).json({ error: 'Internal server error' })
})

app.listen(3000)
```

---

## 5. Fastify (high-performance alternative)

```ts
import Fastify from 'fastify'
import { z } from 'zod'

const app = Fastify({ logger: true })

// Schema validation (JSON Schema — built-in)
app.post<{ Body: { name: string; email: string } }>('/users', {
  schema: {
    body: {
      type: 'object',
      required: ['name', 'email'],
      properties: {
        name: { type: 'string' },
        email: { type: 'string', format: 'email' },
      },
    },
    response: {
      201: { type: 'object', properties: { id: { type: 'string' } } },
    },
  },
  handler: async (req, reply) => {
    const user = await db.user.create({ data: req.body })
    reply.status(201).send(user)
  },
})

// Plugins
await app.register(import('@fastify/cors'), { origin: true })
await app.register(import('@fastify/jwt'), { secret: process.env.JWT_SECRET! })

// Hooks
app.addHook('onRequest', async (req, reply) => {
  // runs before route handler
})

await app.listen({ port: 3000, host: '0.0.0.0' })
```

---

## 6. Hono (edge-ready, ultra-light)

```ts
import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'

const app = new Hono()

// Middleware
app.use('*', cors())
app.use('/api/*', bearerAuth({ token: process.env.API_TOKEN! }))

// Routes
const userRoute = new Hono()
  .get('/', async c => {
    const users = await db.user.findMany()
    return c.json(users)
  })
  .post('/', zValidator('json', z.object({ name: z.string(), email: z.string().email() })), async c => {
    const body = c.req.valid('json')
    const user = await db.user.create({ data: body })
    return c.json(user, 201)
  })
  .get('/:id', async c => {
    const user = await db.user.findUnique({ where: { id: c.req.param('id') } })
    if (!user) return c.json({ error: 'Not found' }, 404)
    return c.json(user)
  })

app.route('/api/users', userRoute)

export default app   // works on Cloudflare Workers, Bun, Node.js, Deno
```

---

## 7. Streams

```ts
import { pipeline } from 'node:stream/promises'
import { createReadStream, createWriteStream } from 'node:fs'
import { createGzip } from 'node:zlib'
import { Transform } from 'node:stream'

// Compress a file
await pipeline(
  createReadStream('./input.log'),
  createGzip(),
  createWriteStream('./input.log.gz')
)

// Custom transform stream
const uppercase = new Transform({
  transform(chunk, encoding, callback) {
    this.push(chunk.toString().toUpperCase())
    callback()
  }
})

// Async iterator over stream
const readable = createReadStream('./data.csv', 'utf-8')
for await (const chunk of readable) {
  process.stdout.write(chunk)
}
```

---

## 8. Environment & Configuration

```ts
// .env loading (Node 20.6+ built-in)
// node --env-file=.env server.js

// Or with dotenv
import 'dotenv/config'

// Type-safe env with Zod
import { z } from 'zod'

const envSchema = z.object({
  NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
  PORT: z.coerce.number().default(3000),
  DATABASE_URL: z.string().url(),
  JWT_SECRET: z.string().min(32),
  REDIS_URL: z.string().optional(),
})

export const env = envSchema.parse(process.env)
// env.PORT is number, not string
```

---

## 9. Error Handling Patterns

```ts
// Async error wrapper for Express
const asyncHandler = (fn: RequestHandler) =>
  (req: Request, res: Response, next: NextFunction) =>
    Promise.resolve(fn(req, res, next)).catch(next)

app.get('/users/:id', asyncHandler(async (req, res) => {
  const user = await db.user.findUnique({ where: { id: req.params.id } })
  if (!user) throw new NotFoundError('User not found')
  res.json(user)
}))

// Custom error classes
class AppError extends Error {
  constructor(public message: string, public statusCode: number, public code?: string) {
    super(message)
    this.name = this.constructor.name
    Error.captureStackTrace(this, this.constructor)
  }
}
class NotFoundError extends AppError {
  constructor(msg = 'Not found') { super(msg, 404, 'NOT_FOUND') }
}
class ValidationError extends AppError {
  constructor(msg: string) { super(msg, 400, 'VALIDATION_ERROR') }
}

// Unhandled rejections & exceptions
process.on('unhandledRejection', (reason) => {
  console.error('Unhandled rejection:', reason)
  process.exit(1)
})
process.on('uncaughtException', (err) => {
  console.error('Uncaught exception:', err)
  process.exit(1)
})
```

---

## 10. Package Management

```bash
# npm
npm init -y
npm install express zod
npm install -D typescript @types/node tsx
npm ci                    # clean install from lock file (CI)
npm audit fix

# pnpm (recommended — faster, disk efficient)
pnpm init
pnpm add express zod
pnpm add -D typescript @types/node tsx
pnpm dlx create-next-app   # like npx but no install

# Workspaces (monorepo)
# pnpm-workspace.yaml:
# packages:
#   - 'apps/*'
#   - 'packages/*'
pnpm --filter @myapp/ui build
pnpm -r run test           # run in all packages
```

### `package.json` Patterns
```json
{
  "type": "module",
  "scripts": {
    "dev": "tsx watch src/index.ts",
    "build": "tsc -p tsconfig.build.json",
    "start": "node dist/index.js",
    "test": "vitest",
    "lint": "eslint src --ext .ts"
  },
  "exports": {
    ".": {
      "import": "./dist/index.js",
      "require": "./dist/index.cjs",
      "types": "./dist/index.d.ts"
    }
  }
}
```

---

## 11. Performance & Clustering

```ts
import cluster from 'node:cluster'
import { availableParallelism } from 'node:os'

if (cluster.isPrimary) {
  const numCPUs = availableParallelism()
  console.log(`Primary ${process.pid} — forking ${numCPUs} workers`)
  for (let i = 0; i < numCPUs; i++) cluster.fork()
  cluster.on('exit', (worker) => {
    console.log(`Worker ${worker.process.pid} died — restarting`)
    cluster.fork()
  })
} else {
  // Worker process — start your server here
  const app = createApp()
  app.listen(3000, () => console.log(`Worker ${process.pid} listening`))
}
```

---

## 12. Testing (Vitest / Jest)

```ts
import { describe, it, expect, vi, beforeEach } from 'vitest'
import request from 'supertest'
import { app } from '../src/app'

describe('GET /api/users', () => {
  beforeEach(() => vi.clearAllMocks())

  it('returns list of users', async () => {
    vi.spyOn(db.user, 'findMany').mockResolvedValue([{ id: '1', name: 'Alice' }])
    const res = await request(app).get('/api/users').expect(200)
    expect(res.body).toHaveLength(1)
    expect(res.body[0].name).toBe('Alice')
  })

  it('returns 401 without auth token', async () => {
    await request(app).get('/api/users/me').expect(401)
  })
})
```

---

## Core Competency Summary

- Understand the Node.js event loop, microtasks, and async model
- Use ESM modules and core Node.js built-ins (`node:fs`, `node:path`, `node:stream`)
- Build REST APIs with Express, Fastify, or Hono with validation and error handling
- Work with streams, worker threads, and child processes
- Configure type-safe environment variables with Zod
- Manage packages with npm/pnpm, including monorepo workspaces
- Test Node.js APIs with Vitest and Supertest
- Scale with clustering and optimize for production

