- ช่วงหลังมานี้ Node.js พัฒนาอย่างรวดเร็วด้วยการ ฝังความสามารถที่เดิมทำได้ผ่านแพ็กเกจ npm เท่านั้นเข้าไว้ในรันไทม์
- สิ่งนี้ช่วยให้ ลดความเสี่ยงด้านความปลอดภัยของซัพพลายเชน, เพิ่มความสามารถในการพกพาโค้ด, ลดการพึ่งพาไลบรารี, และ ทำให้การบำรุงรักษาง่ายขึ้น พร้อมช่วยสร้างประสิทธิภาพและเสถียรภาพในสภาพแวดล้อมโปรดักชัน
- มีการเพิ่ม Global API เช่น
fetch(), WebSocket, node:test ทำให้พัฒนาได้โดยไม่ต้องใช้แพ็กเกจดังอย่าง node-fetch, ws, mocha
- การขยายความสามารถของระบบไฟล์ เพิ่มตัวเลือก
fs.glob(), fs.rm(), fs.mkdir() เพื่อแทน glob, rimraf, mkdirp
- ในส่วนของ utility และ API ด้านการเข้ารหัส ได้รวม
crypto.randomUUID(), util.styleText(), atob/btoa ไว้เป็นมาตรฐาน จึงไม่จำเป็นต้องใช้แพ็กเกจอย่าง uuid, chalk
- ความสามารถบางอย่าง (
node:sqlite, URLPattern, --env-file, การรัน TypeScript เป็นต้น) ยังอยู่ใน ขั้นทดลอง
- ประเด็นสำคัญคือจากวิวัฒนาการภายในของ Node.js ทำให้ สามารถพัฒนาแอปพลิเคชันสมัยใหม่ได้ด้วยรันไทม์พื้นฐานเพียงอย่างเดียว
แพ็กเกจ npm หลักที่ถูกแทนที่ด้วยความสามารถในตัวของ Node.js
- ที่ผ่านมา Node.js พึ่งพาแพ็กเกจ npm หลากหลายตัว เช่น HTTP utility และตัวช่วยระบบไฟล์
- แต่ในเวอร์ชันหลัง ๆ (v18~v22) แนวโน้มการ รวมความสามารถเหล่านี้เข้ามาในรันไทม์โดยตรง ชัดเจนขึ้นเรื่อย ๆ
- ส่งผลให้ความซับซ้อนของการจัดการแพ็กเกจและความเสี่ยงด้านความปลอดภัยลดลง
1. node-fetch → Global fetch()
- ตั้งแต่ Node.js 18 เป็นต้นมา มีการให้
fetch() แบบเดียวกับเบราว์เซอร์ เป็นฟังก์ชัน global
- จัดการคำขอ HTTP ได้โดยไม่ต้องใช้
node-fetch
- เริ่มนำเข้ามาแบบทดลองใน v17.5.0 และเสถียรใน v18.0.0
- อย่างไรก็ตาม หากเป็นเวอร์ชันก่อน 18 ก็ยังต้องใช้
node-fetch
2. ws → Global WebSocket
- ตั้งแต่ Node.js 21 เป็นต้นมา รองรับการเชื่อมต่อ WebSocket ฝั่งไคลเอนต์ผ่าน คลาส global
WebSocket
- เพิ่มเข้ามาแบบทดลองใน v21.0.0 และจนถึงตอนนี้ยังอยู่ในขั้นทดลอง
- สำหรับ การทำ WebSocket ฝั่งเซิร์ฟเวอร์ ยังจำเป็นต้องใช้แพ็กเกจ
ws หรือไลบรารีที่เกี่ยวข้อง
3. เฟรมเวิร์กทดสอบ → node:test
- ตั้งแต่ Node.js 18 เป็นต้นมา มี ตัวรันทดสอบในตัว
node:test ที่ใช้แทน mocha, jest เป็นต้น
- เริ่มนำมาใช้แบบทดลองใน v18.0.0 และเสถียรใน v20.0.0
- รองรับการเขียนและรัน unit test พื้นฐาน
- หากต้องการ snapshot, mocking, ปลั๊กอินที่หลากหลาย เฟรมเวิร์กจากภายนอกก็ยังมีประโยชน์
- เพียงพอสำหรับการทดสอบระดับโมดูล แต่สำหรับการพัฒนาแอปพลิเคชันฟูลสแตก เฟรมเวิร์กเดิมยังมีข้อดีอยู่
4. sqlite3 / better-sqlite3 → node:sqlite
- Node.js กำลังเพิ่ม โมดูล
node:sqlite แบบทดลอง สำหรับเข้าถึง SQLite
- ช่วยแก้ปัญหาการคอมไพล์และข้อผิดพลาดเวลาอัปเกรดที่เกิดจากแพ็กเกจ native binding แบบเดิม
- รองรับงานพื้นฐาน เช่น การสร้างฐานข้อมูลในหน่วยความจำและการสร้างตาราง
- เนื่องจากยังอยู่ในขั้นทดลอง หากต้องการ การจูนประสิทธิภาพขั้นสูงหรือความสามารถเพิ่มเติม แนะนำให้ใช้แพ็กเกจจากคอมมูนิตี้
5. chalk / kleur → util.styleText()
- ตั้งแต่ Node.js 20.12.0 เป็นต้นมา สามารถ จัดรูปแบบข้อความคอนโซลด้วย
util.styleText() ได้
- เสถียรใน v22.17.0
- ใช้ใส่สี ตัวหนา ขีดเส้นใต้ และสไตล์ข้อความพื้นฐานอื่น ๆ
- หากต้องการ ธีมซับซ้อน, syntax แบบ chaining, หรือความเข้ากันได้ย้อนหลัง ก็ยังใช้
chalk ต่อได้
6. strip-ansi → util.stripVTControlCharacters()
- Node.js มีฟังก์ชันในตัวสำหรับ ลบโค้ด ANSI escape
- ช่วยลบอักขระควบคุมจากล็อกได้อย่างปลอดภัย
- ครอบคลุมกรณีใช้งานส่วนใหญ่ได้ในระดับ native จึงไม่จำเป็นต้องใช้แพ็กเกจจากภายนอก
7. glob → fs.glob()
- ตั้งแต่ Node.js 22 เป็นต้นมา เพิ่ม
fs.glob() ในตัวสำหรับค้นหาไฟล์ตามแพตเทิร์น
- เพิ่มใน v22.0.0 และเสถียรใน v22.17.0 LTS
- ค้นหาไฟล์ด้วย glob pattern อย่าง
**/*.js ได้
- หากต้องรองรับ Node.js เวอร์ชันเก่า ก็ยังควรใช้แพ็กเกจ
glob
8. rimraf → fs.rm({ recursive: true })
- รองรับ การลบไดเรกทอรีแบบ recursive ผ่าน Node.js native API
- ใช้งานผ่านตัวเลือก
recursive, force ของ fs.rm()
- ใช้ได้ตั้งแต่ราว ๆ v12.10.0 และตอนนี้เสถียรในทุกเวอร์ชัน LTS
- ลบไดเรกทอรีได้อย่างปลอดภัยโดยไม่ต้องมีแพ็กเกจ
rimraf
9. mkdirp → fs.mkdir({ recursive: true })
- รองรับ การสร้างไดเรกทอรีแบบ recursive ผ่านตัวเลือก
recursive ของ fs.mkdir()
- เพิ่มมาตั้งแต่ v10.12.0 และเสถียรมานานแล้ว
- สร้างไดเรกทอรีซ้อนกันได้โดยไม่ต้องใช้แพ็กเกจแยก
10. uuid → crypto.randomUUID()
- ตั้งแต่ Node.js 14.17.0 เป็นต้นมา มี ฟังก์ชันสร้าง UUID
crypto.randomUUID()
- รวมอยู่ในโมดูลเข้ารหัสที่เสถียร
- สร้าง random ID ที่ปลอดภัยได้โดยไม่ต้องใช้แพ็กเกจ
uuid
11. base64-js / atob → atob, btoa
- ตั้งแต่ Node.js 20 เป็นต้นมา มี ฟังก์ชัน global
atob, btoa
- เป็น API เข้ารหัส/ถอดรหัส Base64 แบบเดียวกับเบราว์เซอร์
- เป็นตัวเลือกเพิ่มเติมนอกเหนือจาก
Buffer ที่มีอยู่เดิม
- จัดการ Base64 ได้โดยไม่ต้องมี polyfill
12. url-pattern → URLPattern
- ตั้งแต่ Node.js 20 เป็นต้นมา มี Global API
URLPattern แบบทดลอง
- รองรับการจับคู่ route การดึง path parameter เป็นต้น
- ยังอยู่ในขั้นทดลองและต้องรอให้เสถียร
- จัดการการจับคู่ URL pattern ได้ด้วยมาตรฐาน Web API
13. dotenv → --env-file แฟลก
- ตั้งแต่ Node.js 20.10.0 เป็นต้นมา รองรับ การโหลดตัวแปรสภาพแวดล้อมผ่านแฟลก
--env-file
- โหลดไฟล์
.env ได้โดยตรงตอนรัน
- ยังอยู่ในขั้นทดลอง
- หากต้องการความสามารถขั้นสูงอย่าง การขยายตัวแปร, การใช้ไฟล์ env หลายไฟล์ ก็ยังต้องใช้แพ็กเกจ
dotenv
14. event-target-shim → EventTarget
- ตั้งแต่ Node.js 15.0.0 เป็นต้นมา มีระบบอีเวนต์มาตรฐานเว็บ
EventTarget แบบ global
- เสถียรใน v15.4.0
- รองรับโมเดลจัดการอีเวนต์แบบเดียวกับเบราว์เซอร์
- ใช้เป็นทางเลือกแทน
EventEmitter ได้
15. tsc → การรัน TypeScript ใน Node.js
- ตั้งแต่ Node.js 21 เป็นต้นมา สามารถ รันไฟล์
.ts ได้โดยตรงด้วยแฟลก --experimental-strip-types
- ทำเพียงลบ type ออก แต่ยังไม่รองรับการตรวจ type แบบเต็มรูปแบบ
- ยังอยู่ในขั้นทดลอง
- สำหรับ โปรดักชันบิลด์, การตรวจสอบ static type, การสร้าง declaration file, การตรวจ type แบบสมบูรณ์ ก็ยังจำเป็นต้องใช้
tsc
บทสรุป
- ทิศทางการพัฒนาของ Node.js คือ ลดการพึ่งพาแพ็กเกจภายนอกและเพิ่มความสมบูรณ์ให้กับแพลตฟอร์มเอง
- ใน LTS เวอร์ชันล่าสุด (v22) มีแพ็กเกจ npm จำนวนมากที่ไม่จำเป็นแล้ว
ซึ่งหมายถึงการปรับปรุงครั้งใหญ่ในด้านความปลอดภัย ประสิทธิภาพ และความสามารถในการบำรุงรักษา
- ในสภาพแวดล้อมการใช้งานระดับองค์กร มีการใช้โซลูชันอย่าง N|Solid เพื่อติดตามการเปลี่ยนแปลงเหล่านี้แบบเรียลไทม์
- วิเคราะห์ประสิทธิภาพของเวิร์กโหลดจริงสำหรับความสามารถในตัว (
fetch, node:test, crypto.randomUUID() เป็นต้น)
- เอเจนต์ AI N|Sentinel ช่วยติดตามการใช้งานและให้คำแนะนำการปรับแต่งในระดับโค้ด
6 ความคิดเห็น
API ที่รันไทม์มีให้เพิ่มขึ้นเรื่อย ๆ..
โดยเฉพาะพวก network API, URL, b64 และ API สำหรับจัดการสตริงต่าง ๆ...
นี่มัน... PH..
ดูเหมือนว่า Uuid v7 จะยังมาเร็วเกินไปอยู่ดีนะ
ผมคิดว่าไดรเวอร์ฐานข้อมูลควรมองต่างออกไปจากฟีเจอร์ที่ควรเข้าไปอยู่ในไลบรารีพื้นฐานตัวอื่น ๆ นะ แต่ทั้ง bun เองก็ด้วย ทำไมถึงพยายามฝัง sqlite driver เข้าไปในรันไทม์กันล่ะ?
เป็นเพราะ Python มีมาให้ในตัวหรือเปล่า?
ผมคิดว่าสิ่งที่สำคัญกว่าการฝังความสามารถของ sqlite เข้าไป คือการสร้างมาตรฐานอินเทอร์เฟซของ db driver
อินเทอร์เฟซของแต่ละไดรเวอร์ไม่เหมือนกัน เลยกลายเป็นว่าถ้าจะรองรับฐานข้อมูลหลายประเภท ก็ยากถ้าไม่ใช้ ORM ไม่ใช่เหรอ
น่าจะใส่มาเพราะบริการขนาดเล็กระดับบล็อกส่วนตัวมักแค่ใช้ sqlite ก็เพียงพอแล้ว ถ้ามีก็สะดวกดีครับ
ผมคิดว่าน่าจะใส่
sqliteมาเพราะมีอินเทอร์เฟซที่เรียบง่ายที่สุดและเป็นตัวอย่างอ้างอิงที่ดูได้ง่ายที่สุดครับแล้วก็ผมไม่ค่อยเข้าใจว่าทำไมถึงต้องสร้างมาตรฐานสำหรับอินเทอร์เฟซของ DB Driver อย่างที่คุณพูดถึงนะครับ คล้าย ๆ กันแบบนี้เหมือนจะเคยเห็นใน PHP อยู่บ้าง
คิวรีที่ซับซ้อนซึ่ง ORM จัดการไม่ได้ ทุกวันนี้ก็ยังใช้ RAW query กันอยู่...
ดูท่าคุณคงมีโอกาสต้องใช้ DB หลายประเภทบ่อยพอสมควรเลยนะครับ... ลองทำไลบรารีขึ้นมาเองดีไหมครับ? :)
กรณีที่รันไทม์สร้างมาตรฐานของอินเทอร์เฟซ db driver มาให้ เช่น python, java, c#, go ฯลฯ ถือว่าเป็นเรื่องที่พบได้ค่อนข้างทั่วไป
แต่สำหรับ node แม้แต่ไดรเวอร์ที่ใช้ sqlite ตัวเดียวกัน ตอนรัน statement ก็ยังต่างกันว่าใช้
execute()หรือexec()ดังนั้นแค่สลับเปลี่ยนไดรเวอร์ก็ยังต้องแก้โค้ดพอสมควรแล้วแม้จะไม่ใช่เรื่องที่เกิดขึ้นบ่อย แต่เวลาเปลี่ยน db ก็ลำบากเหมือนกัน
สมมติว่าใช้ mysql อยู่ แล้วไม่ชอบสิ่งที่ oracle ทำ หรือมีส่วนขยายบางอย่างของ postgresql ที่จำเป็นต้องใช้ เลยจะย้ายไป postgresql
ถ้ามีอินเทอร์เฟซมาตรฐานแบบ jdbc ก็แค่ตรวจสอบ sql แต่ในฝั่ง node มีผลข้างเคียงคือต้องรื้อ logic การเรียกใช้ db ใหม่ทั้งหมด
มีคนแนะนำให้ลองทำไลบรารีขึ้นมา แต่ถ้ามีมาตรฐานของอินเทอร์เฟซกลาง เวลาเขียนไลบรารีก็จะสะดวกกว่านะครับ
ที่บริษัทใช้ java และในเฟรมเวิร์กภายในองค์กรต้องรองรับ mysql, db2, oracle, mssql เลยได้ประโยชน์จากมาตรฐาน jdbc มากตอนต้องดูแลรักษา adapter ของแต่ละ db