Skip to content

บทที่ 2 — Core Modules + process: ของแถมที่ Node ให้มาฟรี

← บทที่ 1: Modules + npm | สารบัญ | บทที่ 3: Async + Events →

Node มาพร้อม core modules (หรือ built-in modules) — library ที่ติดมากับ Node เลย ไม่ต้อง npm install เช่น fs, http, path, os, crypto, process บทนี้โฟกัสตัวที่ backend ใช้ทุกวัน + global objects ที่มีให้ใช้โดยไม่ต้อง import

หัวข้อหนัก ๆ อย่าง fs/http/stream มีบทของตัวเอง (บท 4–5) — บทนี้ปูพื้น process, path, os, util และ global ก่อน

💡 ของใหม่ที่ Node 22 LTS แถมมา (เจาะลึกในบทถัด ๆ ไป): node:test (test runner ในตัว ไม่ต้องลง Jest), node:sqlite (SQLite client ในตัว — ตรวจสอบสถานะ/เวอร์ชันล่าสุดในเอกสารทางการก่อนใช้จริง เพราะสถานะ stable ของ module นี้เปลี่ยนไปตามเวอร์ชัน Node), node:stream/web (Web Streams API), util.parseArgs (CLI argument parser ในตัว), Permission Model (--permission จำกัดสิทธิ์ filesystem/network ของโปรเซส), built-in fetch, --watch, --env-file


1. core module คืออะไร และเรียกใช้ยังไง

core module = module ที่ฝังมากับตัว Node — เร็ว, ไม่ต้องโหลด, ไม่ขึ้นกับ npm ของใหม่ใน 2026 ให้ใช้ node: prefix เสมอ (ป้องกันการโดน "หลอก" ด้วย package ใน npm ที่ตั้งชื่อทับซ้อนกับ built-in เช่น มีคนอัปโหลด package ชื่อ fs ขึ้น npm — node:fs รับประกันว่าได้ตัวจริงจาก Node):

javascript
import { readFile } from "node:fs/promises";   // ✅ มาตรฐาน 2026
import path from "node:path";
import os from "node:os";

// เขียนแบบไม่มี node: prefix เป็นของเก่า — ยังใช้ได้แต่ไม่แนะนำในโค้ดใหม่
// import fs from "fs";   ← ใช้ได้ แต่ไม่แนะนำ

core module ที่เจอบ่อย:

  • fs — อ่าน/เขียนไฟล์
  • path — จัดการ path ข้าม OS
  • os — ข้อมูลเครื่อง (CPU, RAM)
  • http/https — ตัว server/client (ใช้ทำ web server, ยิง request)
  • url — parse/ประกอบ URL
  • crypto — เข้ารหัส, hash, สุ่มเลข
  • events — EventEmitter (publish/subscribe pattern — บทที่ 3)
  • stream — ส่งข้อมูลทีละชิ้น (บทที่ 5)
  • util — ฟังก์ชันช่วยเหลือ (promisify, inspect, styleText, parseArgs)
  • process — global ไม่ต้อง import (ดูข้อ 3)

2. global objects — ของที่ใช้ได้โดยไม่ต้อง import

ในเบราว์เซอร์ตัวแปร global อยู่ใต้ window — ใน Node อยู่ใต้ global (หรือ globalThis ที่ใช้ได้ทั้งสองที่) ของพวกนี้ใช้ได้เลยไม่ต้อง import:

javascript
console.log("...");        // เหมือนเบราว์เซอร์
setTimeout(fn, 1000);      // timers — เหมือนเบราว์เซอร์
setInterval(fn, 1000);
queueMicrotask(fn);
fetch("https://...");      // Node 18+ มี fetch built-in แล้ว (เหมือนเบราว์เซอร์)
structuredClone(obj);      // deep clone object
URL, URLSearchParams;      // จัดการ URL

// เฉพาะ Node (ไม่มีในเบราว์เซอร์):
process;                   // ข้อมูล + ควบคุม process (ข้อ 3)
Buffer;                    // ข้อมูล binary (บทที่ 4)
global;                    // = window ของฝั่ง Node

⚠️ Pitfall: ใน CommonJS มี __dirname, __filename, require, module, exports เป็น global-ish ให้ใช้ — แต่ใน ESM ไม่มี (ดูบทที่ 1) ใช้ import.meta.dirname/import.meta.url แทน


3. process — object ที่สำคัญที่สุดของ backend

process คือ global object ที่แทน "process ของ Node ที่กำลังรันอยู่ตอนนี้" — เป็นสะพานเชื่อมโปรแกรมกับโลกภายนอก (ระบบปฏิบัติการ, environment, คนที่สั่งรัน) ใช้บ่อยมากใน backend

3.1 process.env — environment variables (หัวใจของ config)

process.env คือ object ของ environment variable (= ตัวแปรของระบบ/shell ที่ส่งให้โปรเซสตอนรัน) ทั้งหมด — วิธีมาตรฐานในการส่ง config เข้าโปรแกรม (เช่น รหัส DB, port, โหมด) โดยไม่ฮาร์ดโค้ดลงไฟล์ (บทที่ 7 จะลงลึกเรื่อง config):

javascript
// อ่านค่า — ได้ string หรือ undefined เสมอ
console.log(process.env.NODE_ENV);     // เช่น 'production' หรือ undefined
console.log(process.env.HOME);         // path home (Windows: process.env.USERPROFILE)

// pattern ที่ใช้จริง: อ่าน + ใส่ค่า default
const PORT = process.env.PORT ?? 3000;             // ถ้าไม่ตั้งไว้ ใช้ 3000
const isProd = process.env.NODE_ENV === "production";

ลองส่ง env ตอนรัน — syntax ต่างกันตาม shell:

bash
# macOS/Linux (bash/zsh): ตั้ง env แบบ inline ผูกกับคำสั่งเดียวเท่านั้น
PORT=8080 NODE_ENV=production node app.js
powershell
# Windows PowerShell: ตั้งใน session ปัจจุบัน (จะค้างอยู่จนปิด terminal)
# $env:NAME = วิธีอ่าน/เขียน environment variable ของ session ใน PowerShell
$env:PORT = "8080"
$env:NODE_ENV = "production"
node app.js
# ถ้าอยากแบบ "ตั้งครั้งเดียวสำหรับคำสั่งนี้" ต้องใช้ scriptblock + Start-Process หรือเครื่องมือเสริม
# วิธีง่ายสุดในปี 2026: ใช้ --env-file (ด้านล่าง)

💡 วิธี 2026 ที่แนะนำ: Node 20.6+ มี flag --env-file=<path> (และ --env-file-if-exists ไม่ error ถ้าไม่มีไฟล์) โหลด .env ได้โดยตรง ไม่ต้องลง dotenv:

bash
node --env-file=.env app.js

ในไฟล์ .env:

PORT=8080
NODE_ENV=production

⚠️ process.env.X คืน string เสมอprocess.env.PORT ได้ "8080" (string) ไม่ใช่ 8080 (number) ถ้าต้องการตัวเลขต้อง Number(process.env.PORT) · ค่าที่ไม่ตั้งไว้คือ undefined ไม่ใช่ error

3.2 process.argv — command line arguments

process.argv คือ array ของสิ่งที่พิมพ์ตอนรัน — ใช้ทำ CLI tool (บทที่ 7 ทำ tool เต็ม ๆ):

javascript
// รัน: node app.js hello --verbose
console.log(process.argv);
// [
//   '/path/to/node',     ← argv[0] = path ของตัว node ที่กำลังรัน
//   '/path/to/app.js',   ← argv[1] = path ของไฟล์สคริปต์เรา
//   'hello',             ← argv[2] = argument ตัวแรกที่ user พิมพ์เอง
//   '--verbose'          ← argv[3] = argument ตัวที่สอง
// ]
// ทำไมต้องตัด 2 ตัวแรก? เพราะ argv[0] กับ argv[1] เป็น "บริบทการรัน"
// ของ Node เอง ไม่ใช่ของที่ user ตั้งใจส่งให้สคริปต์

const args = process.argv.slice(2);   // ตัด 2 ตัวแรกออก เหลือเฉพาะของเรา
console.log(args);                     // ['hello', '--verbose']

💡 วิธี 2026: ถ้าต้อง parse flag (--name=value, --flag, positional args) ใช้ util.parseArgs (Node 18.3+) ที่มีในตัว — เลิกพึ่ง library อย่าง yargs/commander สำหรับงานเล็ก ๆ (ดูบทที่ 7)

3.3 process.exit + exit code

process.exit(code) สั่งจบโปรแกรมทันที — 0 = สำเร็จ, ตัวเลขอื่น = ผิดพลาด ค่านี้เรียกว่า exit code และ shell กับเครื่องมืออัตโนมัติใช้มันตัดสินว่าคำสั่งสำเร็จไหม เช่น && ของ bash จะรันคำสั่งถัดไปก็ต่อเมื่อ exit code = 0 เท่านั้น CI (ย่อจาก Continuous Integration) คือระบบที่รันคำสั่ง build/test ให้อัตโนมัติทุกครั้งที่มีการ push โค้ด — มันก็อาศัย exit code นี้เป็นตัวตัดสินเช่นกันว่างานผ่านหรือไม่ผ่าน:

javascript
if (!process.env.DATABASE_URL) {
  console.error("❌ ต้องตั้ง DATABASE_URL");
  process.exit(1);     // จบด้วย exit code 1 = error
}

⚠️ Pitfall: process.exit() จบ ทันที — งาน async ที่ค้างอยู่ (เขียนไฟล์/log ที่ยังไม่เสร็จ) จะถูกตัดกลางคัน ในกรณีปกติ อย่าเรียก process.exit() เอง ปล่อยให้โปรแกรมจบเองเมื่อไม่มีงานค้าง (event loop ว่าง) — ใช้ process.exit() เฉพาะตอน fatal error จริง ๆ เท่านั้น

3.4 process อื่น ๆ ที่เจอบ่อย

javascript
process.cwd();        // current working directory — โฟลเดอร์ที่ "รันคำสั่ง" (ไม่ใช่ที่อยู่ไฟล์!)
process.pid;          // process id — เลขประจำตัว process
process.platform;     // 'darwin' | 'win32' | 'linux'
process.version;      // เวอร์ชัน Node เช่น 'v22.1.0'
process.uptime();     // โปรแกรมรันมากี่วินาทีแล้ว
process.memoryUsage();// การใช้ memory (ดู heap — บทที่ 10)

⚠️ จุดพลาดคลาสสิก: สับสนระหว่าง process.cwd() (โฟลเดอร์ที่ "รันคำสั่ง") กับ import.meta.dirname (โฟลเดอร์ที่ "ไฟล์อยู่") — ถ้ารัน node src/app.js จากราก: process.cwd() = ราก แต่ import.meta.dirname = .../src ใช้ผิดตัวจะหาไฟล์ไม่เจอ

3.5 process events — ดักจังหวะสำคัญ

process ยิง event ตอนเหตุการณ์สำคัญเกิดขึ้น — สำคัญมากตอนทำ graceful shutdown (= ปิดโปรแกรมอย่างเรียบร้อย ไม่ใช่ตายดิบทันที) หมายถึงก่อนโปรแกรมจะจบ ต้องปิด connection กับ DB ให้เรียบร้อย ปิด server ไม่ให้รับ request ใหม่ และรอส่ง response ที่ค้างอยู่ให้เสร็จก่อน (บทที่ 7 อธิบายเต็ม) เรื่องนี้เกี่ยวโยงกับ error handling ด้วย (บทที่ 6):

javascript
// ดักสัญญาณปิดโปรแกรม เพื่อปิดให้เรียบร้อยก่อนตาย
// SIGTERM = signal มาตรฐาน "ขอให้ปิดอย่างสุภาพ" ที่ระบบยิงให้โปรเซส
// เช่น เวลา `docker stop` (สั่งให้ container หยุด), kubernetes rolling deploy, systemctl stop
// ส่วน Ctrl+C ในเทอร์มินัลจะยิง SIGINT (เป็น event แยก แต่ดักลักษณะเดียวกัน)
process.on("SIGTERM", () => {
  console.log("กำลังปิด server อย่างนุ่มนวล...");
  // ปิด DB, ปิด server, แล้วค่อยจบ
});

// ดัก error ที่หลุดมาไม่มีใครจับ (ไม้ตายสุดท้าย — บทที่ 6 อธิบาย)
// ⚠️ ทำไมต้อง exit(1) ไม่พยายาม "เก็บกวาดแล้วทำงานต่อ"?
// เพราะ uncaughtException แปลว่า "มีอะไรพังถึงระดับที่หลุดมาถึงตรงนี้" —
// state ภายในของโปรแกรม (ตัวแปร, connection, GC) อาจเสียหายไม่สามารถวางใจได้
// วิธีที่ปลอดภัยคือ log แล้วจบทันที ให้ตัว orchestrator (docker/k8s/pm2) restart ใหม่
process.on("uncaughtException", (err) => {
  console.error("error ที่ไม่ได้จับ:", err);
  process.exit(1);
});

// คู่กับ uncaughtException: ดัก Promise ที่ reject แล้วไม่มีใคร .catch()
// ตั้งแต่ Node 15+ default = terminate อยู่แล้ว แต่ใส่ handler เพื่อ log
process.on("unhandledRejection", (reason) => {
  console.error("Promise ที่ reject โดยไม่มีใครจับ:", reason);
  process.exit(1);
});

4. path — จัดการ path ให้ถูกทุก OS

อย่าต่อ path ด้วยการเอา string มาบวก "folder" + "/" + "file" — เพราะ Windows ใช้ \ แต่ macOS/Linux ใช้ / โค้ดจะพังข้ามเครื่อง ใช้ path เสมอ มันเลือกตัวคั่นให้ถูกตาม OS:

javascript
import path from "node:path";

// join — ต่อ path อย่างถูกต้อง (ใส่ตัวคั่นให้เอง)
path.join("/users", "somchai", "data.json");
// macOS/Linux → '/users/somchai/data.json'
// Windows     → '\users\somchai\data.json'

// resolve — แปลงเป็น absolute path (อิงจาก cwd ถ้าไม่ใช่ absolute)
path.resolve("src", "app.js");          // → '/current/dir/src/app.js'

// แยกส่วนของ path
path.basename("/a/b/file.txt");         // 'file.txt'  ← ชื่อไฟล์
path.dirname("/a/b/file.txt");          // '/a/b'      ← โฟลเดอร์
path.extname("/a/b/file.txt");          // '.txt'      ← นามสกุล
path.parse("/a/b/file.txt");            // { dir, base, name: 'file', ext: '.txt' }

// pattern ใช้จริง: หาไฟล์ที่อยู่ "ข้าง ๆ" ไฟล์โค้ดเรา (ESM)
const configPath = path.join(import.meta.dirname, "config.json");

💡 จำง่าย: join = ต่อชิ้นส่วน path · resolve = ทำให้เป็น absolute · ใช้ import.meta.dirname เป็นจุดอ้างอิงเมื่ออยากชี้ไฟล์ที่อยู่ "ข้างโค้ด" และ process.cwd() เมื่ออยากอิง "ที่ user รันคำสั่ง"


5. os — ข้อมูลเครื่องที่รันอยู่

os บอกข้อมูลฮาร์ดแวร์/ระบบ — ใช้ตอน optimize หรือ health check เช่น เปิด worker หลายตัวให้เท่าจำนวน CPU core (worker = โปรเซสลูกที่รันคู่ขนานกันเพื่อแบ่งโหลดงาน ส่วน cluster คือกลไกของ Node ที่ช่วยสร้าง/จัดการ worker หลายตัวเหล่านี้ให้อัตโนมัติ — รายละเอียดเต็มดูบทที่ 10):

javascript
import os from "node:os";

os.cpus().length;       // จำนวน CPU core เช่น 8 → ใช้ตั้งจำนวน worker/cluster
os.totalmem();          // RAM ทั้งหมด (bytes)
os.freemem();           // RAM ว่าง (bytes)
os.hostname();          // ชื่อเครื่อง
os.platform();          // เหมือน process.platform
os.tmpdir();            // โฟลเดอร์ temp ของระบบ (ที่เขียนไฟล์ชั่วคราวได้)
os.homedir();           // home directory ของ user

6. util — เครื่องมือสารพัดประโยชน์

util มีฟังก์ชันช่วยเหลือหลายอย่าง — ตัวที่ใช้บ่อยสุดคือ promisify (แปลง callback API เก่าให้เป็น Promise) ตัวอย่างข้างล่างมีการใช้ await ซึ่งเป็นเรื่องของ Promise ที่บทที่ 3 จะอธิบายเต็ม ๆ — ตอนนี้ยังไม่ต้องเข้าใจลึก แค่ทำตามตัวอย่างและรู้ว่ามันแปลง callback ให้เขียนแบบ await ได้ก็พอ:

javascript
import util from "node:util";

// inspect — แปลง object เป็น string สวย ๆ สำหรับ debug (เจาะ nested object ลึก ๆ)
const obj = { a: { b: { c: { d: 1 } } } };
console.log(util.inspect(obj, { depth: null, colors: true }));  // เห็นครบทุกชั้น

// promisify.mjs — ต้องเป็นไฟล์ ESM (.mjs หรือมี "type": "module") เพราะมี top-level await ข้างล่าง
// promisify — แปลง function แบบ callback ให้เป็นแบบ Promise (บทที่ 3 อธิบาย Promise/await เต็ม)
import { readFile } from "node:fs";
const readFileAsync = util.promisify(readFile);
// ⚠️ บรรทัดถัดไปใช้ top-level await — ต้องอยู่ในไฟล์ ESM เท่านั้น
// (มี "type": "module" ใน package.json หรือไฟล์ .mjs — ดูบทที่ 1)
const data = await readFileAsync("file.txt", "utf8");

// styleText — ใส่สีให้ข้อความใน terminal โดยไม่ต้องลง library (เช่น chalk)
// เพิ่มเข้ามาตั้งแต่ Node 20.12 / 21.7 — ใน Node 22 LTS ใช้งานได้ stable แล้ว
console.log(util.styleText("green", "✅ สำเร็จ"));

// parseArgs (Node 18.3+) — parse CLI flag ในตัว ทดแทน yargs/commander สำหรับงานเล็ก
const { values, positionals } = util.parseArgs({
  options: { verbose: { type: "boolean", short: "v" } },
  allowPositionals: true,
});
// node app.js hello -v  → values = { verbose: true }, positionals = ['hello']

7. ตัวอย่างรวม — สคริปต์ที่ใช้หลาย core module

เอามาประกอบกันเป็นสคริปต์จริง — รายงานข้อมูลระบบออกไฟล์:

javascript
// sysreport.mjs — รายงานข้อมูลเครื่องลงไฟล์ JSON
import os from "node:os";
import path from "node:path";
import { writeFile } from "node:fs/promises";

// 1. รวบรวมข้อมูลจาก os + process
const report = {
  hostname: os.hostname(),
  platform: process.platform,
  nodeVersion: process.version,
  cpuCount: os.cpus().length,
  memoryGB: (os.totalmem() / 1024 ** 3).toFixed(1),   // bytes → GB
  cwd: process.cwd(),
  generatedAt: new Date().toISOString(),
};

// 2. อ่านชื่อไฟล์ output จาก argument (ถ้าไม่ส่ง ใช้ default)
const outName = process.argv[2] ?? "report.json";

// 3. สร้าง path ที่ถูกต้องข้าง ๆ ไฟล์สคริปต์นี้ แล้วเขียนไฟล์
const outPath = path.join(import.meta.dirname, outName);
await writeFile(outPath, JSON.stringify(report, null, 2));   // null,2 = จัดรูปสวย

console.log(`✅ เขียนรายงานที่ ${outPath}`);
bash
$ node sysreport.mjs
 เขียนรายงานที่ /path/report.json
$ node sysreport.mjs myreport.json   # ส่งชื่อไฟล์เอง

🛠️ Checkpoint 2 — ลงมือก่อนไปบทถัดไป

  1. เขียนสคริปต์ที่อ่าน process.argv รับตัวเลข 2 ตัวแล้วพิมพ์ผลบวก เช่น node sum.js 3 47 (ระวัง: argv เป็น string ต้อง Number())
  2. เขียนสคริปต์ที่ตรวจว่า process.env.API_KEY ถูกตั้งไหม — ถ้าไม่ พิมพ์ error แล้ว process.exit(1) · ลองรันทั้งแบบมีและไม่มี env แล้วเช็ก exit code ด้วย echo $? (macOS/Linux bash) หรือ echo $LASTEXITCODE (Windows PowerShell — ใน cmd.exe ใช้ echo %ERRORLEVEL%)
  3. ใช้ path แยก /var/log/app/server.log ออกเป็น dirname / basename / extname
  4. เขียนสคริปต์ sysinfo ที่พิมพ์จำนวน CPU, RAM ว่างเป็น GB, และ uptime ของโปรแกรม
  5. ลองสร้างไฟล์ใน src/app.js ที่ console.log(process.cwd()) และ console.log(import.meta.dirname) แล้วรันจากโฟลเดอร์ราก (node src/app.js) — สังเกตว่าได้ค่าต่างกัน เข้าใจว่าทำไม

เฉลยข้อ 1:

javascript
const [a, b] = process.argv.slice(2).map(Number);   // แปลง string → number
console.log(a + b);

สรุปบทที่ 2

  • core module = built-in มากับ Node ไม่ต้อง install — เรียกด้วย node: prefix (เช่น node:fs)
  • global ใช้ได้เลยไม่ต้อง import: console, setTimeout, fetch, process, Buffer, globalThis
  • process = สะพานเชื่อมโปรแกรมกับโลกภายนอก:
    • process.env = config ผ่าน environment (คืน string เสมอ)
    • process.argv = command line arguments
    • process.exit(code) = จบโปรแกรม (0 สำเร็จ) — เลี่ยงเรียกเองนอกจาก fatal
    • process.on("SIGTERM"/"uncaughtException") = ดักจังหวะปิด/error
  • path จัดการ path ข้าม OS — join/resolve/basename/dirname · อย่าต่อ path ด้วย string บวกกันเอง
  • os ข้อมูลเครื่อง (CPU, RAM) · util เครื่องมือ (promisify, inspect)
  • ระวังคู่สับสน: process.cwd() (ที่รันคำสั่ง) ≠ import.meta.dirname (ที่ไฟล์อยู่)

บทต่อไปคือหัวใจของ Node — async, callback, Promise, EventEmitter และ event loop เชิงลึก ถ้าผ่านบทนี้ได้ ที่เหลือสบาย

→ บทที่ 3: Async + Events