คำถามสัมภาษณ์ Backend Node.js: คู่มือฉบับสมบูรณ์ 2026
25 คำถามสัมภาษณ์ Backend Node.js ที่พบบ่อยที่สุด Event loop, async/await, streams, clustering และประสิทธิภาพอธิบายพร้อมคำตอบโดยละเอียด

การสัมภาษณ์ Backend Node.js ประเมินความเข้าใจเกี่ยวกับกลไกภายในของ runtime ความชำนาญในรูปแบบ asynchronous และความสามารถในการออกแบบแอปพลิเคชันที่มีประสิทธิภาพสูง คู่มือนี้ครอบคลุมคำถามที่ถูกถามบ่อยที่สุด ตั้งแต่พื้นฐานจนถึงแนวคิดระดับ production ขั้นสูง
ผู้สัมภาษณ์จะประทับใจกับคำตอบที่ผสมผสานทฤษฎีกับตัวอย่างจริง สำหรับแต่ละคำถาม การยกตัวอย่างโค้ดหรือกรณีใช้งานจริงแสดงให้เห็นถึงประสบการณ์ในโลกจริง
พื้นฐาน Node.js
คำถามที่ 1: Event Loop คืออะไรและทำงานอย่างไร?
Event Loop คือกลไกหลักที่ทำให้ Node.js สามารถจัดการการทำงานแบบ asynchronous ในลักษณะ non-blocking แม้จะทำงานบน thread เดียว กลไกนี้จัดเรียงการทำงานของโค้ด JavaScript, callback และ event
// Demonstration of Event Loop execution order
console.log('1. Script start (synchronous)');
// setTimeout goes to the Timer Queue
setTimeout(() => {
console.log('5. setTimeout callback (Timer Queue)');
}, 0);
// setImmediate goes to the Check Queue
setImmediate(() => {
console.log('6. setImmediate callback (Check Queue)');
});
// Promise goes to the Microtask Queue (priority)
Promise.resolve().then(() => {
console.log('3. Promise.then (Microtask Queue)');
});
// process.nextTick has the highest priority
process.nextTick(() => {
console.log('2. process.nextTick (nextTick Queue)');
});
console.log('4. Script end (synchronous)');
// Output order: 1, 4, 2, 3, 5, 6Event Loop ปฏิบัติตามลำดับที่แน่นอนในการประมวลผลคิว: โค้ดแบบ synchronous ก่อน จากนั้น nextTick, microtask (Promise), timer, callback I/O, setImmediate และสุดท้ายคือ close callback
คำถามที่ 2: ความแตกต่างระหว่าง process.nextTick() และ setImmediate() คืออะไร?
คำถามนี้ประเมินความเข้าใจอย่างละเอียดเกี่ยวกับลำดับความสำคัญของการทำงานใน Event Loop
// Behavior comparison
// process.nextTick executes BEFORE the next Event Loop phase
process.nextTick(() => {
console.log('nextTick 1');
process.nextTick(() => {
console.log('nextTick 2 (nested)');
});
});
// setImmediate executes in the Check phase of the Event Loop
setImmediate(() => {
console.log('setImmediate 1');
setImmediate(() => {
console.log('setImmediate 2 (nested)');
});
});
// Output: nextTick 1, nextTick 2, setImmediate 1, setImmediate 2process.nextTick() จะถูกประมวลผลทันทีหลังจากการทำงานปัจจุบัน ก่อนที่ Event Loop จะดำเนินต่อ การใช้มากเกินไปอาจทำให้ Event Loop ถูกบล็อก setImmediate() คาดเดาได้ง่ายกว่าและแนะนำให้ใช้สำหรับการเลื่อนการทำงาน
การเรียก process.nextTick() แบบ recursive อาจทำให้ Event Loop ขาดแคลนและป้องกันการประมวลผล I/O ควรใช้ setImmediate() สำหรับการทำงานที่ไม่สำคัญ
คำถามที่ 3: Node.js จัดการ error ในโค้ด asynchronous อย่างไร?
การจัดการ error แบบ asynchronous แตกต่างอย่างมากจากโค้ดแบบ synchronous หากไม่จัดการอย่างเหมาะสม error สามารถทำให้แอปพลิเคชันล่มได้
// Asynchronous error handling patterns
// Pattern 1: Callbacks with error-first convention
function readFileCallback(path, callback) {
const fs = require('fs');
fs.readFile(path, 'utf8', (err, data) => {
if (err) {
// Error is ALWAYS the first argument
return callback(err, null);
}
callback(null, data);
});
}
// Pattern 2: Promises with catch
async function readFilePromise(path) {
const fs = require('fs').promises;
try {
const data = await fs.readFile(path, 'utf8');
return data;
} catch (err) {
// Centralized error handling
console.error(`File read error: ${err.message}`);
throw err; // Re-throw for propagation
}
}
// Pattern 3: Global handling of unhandled rejections
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled rejection:', reason);
// In production: log and graceful shutdown
});
// Pattern 4: Handling uncaught exceptions
process.on('uncaughtException', (err) => {
console.error('Uncaught exception:', err);
// CRITICAL: always terminate the process after
process.exit(1);
});ในสภาพแวดล้อม production ทุก Promise ต้องมี .catch() หรืออยู่ภายในบล็อก try/catch ตัวจัดการ global ทำหน้าที่เป็นตาข่ายนิรภัย ไม่ใช่วิธีแก้ปัญหาหลัก
การเขียนโปรแกรมแบบ Asynchronous และ Concurrency
คำถามที่ 4: อธิบายความแตกต่างระหว่าง Parallelism และ Concurrency ใน Node.js
Node.js มีคุณสมบัติ concurrent แต่ไม่ใช่ parallel โดยค่าเริ่มต้น ความแตกต่างนี้เป็นพื้นฐานสำคัญในการทำความเข้าใจประสิทธิภาพ
// CONCURRENCY: multiple tasks progress by alternating (single-thread)
async function concurrentTasks() {
console.time('concurrent');
// These calls are concurrent, not parallel
const results = await Promise.all([
fetch('https://api.example.com/users'), // Non-blocking I/O
fetch('https://api.example.com/products'), // Non-blocking I/O
fetch('https://api.example.com/orders'), // Non-blocking I/O
]);
console.timeEnd('concurrent'); // ~time of the longest request
return results;
}
// PARALLELISM: with Worker Threads for CPU-bound tasks
const { Worker, isMainThread, parentPort } = require('worker_threads');
if (isMainThread) {
// Main thread delegates CPU-intensive work
async function parallelComputation() {
console.time('parallel');
const workers = [
createWorker({ start: 0, end: 1000000 }),
createWorker({ start: 1000000, end: 2000000 }),
createWorker({ start: 2000000, end: 3000000 }),
];
const results = await Promise.all(workers);
console.timeEnd('parallel');
return results.reduce((a, b) => a + b, 0);
}
function createWorker(data) {
return new Promise((resolve, reject) => {
const worker = new Worker(__filename, { workerData: data });
worker.on('message', resolve);
worker.on('error', reject);
});
}
} else {
// Code executed in the Worker Thread
const { workerData } = require('worker_threads');
let sum = 0;
for (let i = workerData.start; i < workerData.end; i++) {
sum += Math.sqrt(i); // CPU-intensive calculation
}
parentPort.postMessage(sum);
}สำหรับการทำงานที่ผูกกับ I/O (เครือข่าย, ไฟล์) concurrency ที่มีอยู่ก็เพียงพอ สำหรับงานที่ผูกกับ CPU (การคำนวณหนัก, การเข้ารหัส) Worker Threads ทำให้สามารถทำงานแบบ parallel ได้จริง
คำถามที่ 5: โมดูล Cluster ทำงานอย่างไร?
โมดูล Cluster ช่วยให้สร้าง process Node.js หลายตัวที่ใช้ port เดียวกันร่วมกัน จึงใช้ประโยชน์จาก CPU core ทั้งหมดที่มี
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isPrimary) {
console.log(`Primary ${process.pid} is running`);
console.log(`Forking ${numCPUs} workers...`);
// Fork one worker per CPU core
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
// Handle crashing workers
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died (${signal || code})`);
console.log('Starting a new worker...');
cluster.fork(); // Automatic restart
});
// Inter-process communication
cluster.on('message', (worker, message) => {
console.log(`Message from worker ${worker.id}:`, message);
});
} else {
// Workers share the TCP port
http.createServer((req, res) => {
res.writeHead(200);
res.end(`Handled by worker ${process.pid}\n`);
// Send stats to primary
process.send({ type: 'request', pid: process.pid });
}).listen(8000);
console.log(`Worker ${process.pid} started`);
}การกระจายโหลดจะถูกจัดการโดยอัตโนมัติโดยระบบปฏิบัติการ (round-robin บน Linux/macOS) ในสภาพแวดล้อม production PM2 ทำให้การจัดการนี้ง่ายขึ้นด้วยโหมด cluster ที่มีอยู่ในตัว
พร้อมที่จะพิชิตการสัมภาษณ์ Node.js / NestJS แล้วหรือยังครับ?
ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ
Streams และ Buffers
คำถามที่ 6: ควรใช้ Streams แทนวิธีแบบดั้งเดิมเมื่อใด?
Streams ช่วยให้ประมวลผลข้อมูลเป็นส่วนย่อย (chunk) แทนที่จะโหลดทั้งหมดลงในหน่วยความจำ สิ่งนี้จำเป็นอย่างยิ่งสำหรับไฟล์ขนาดใหญ่และสถานการณ์ streaming
const fs = require('fs');
// ❌ BAD: loads entire file into memory
async function readEntireFile(path) {
const data = await fs.promises.readFile(path); // Blocks if file > RAM
return processData(data);
}
// ✅ GOOD: chunk-based processing with Stream
function readWithStream(path) {
return new Promise((resolve, reject) => {
const chunks = [];
const readStream = fs.createReadStream(path, {
highWaterMark: 64 * 1024, // 64KB per chunk
});
readStream.on('data', (chunk) => {
// Progressive processing, constant memory
chunks.push(processChunk(chunk));
});
readStream.on('end', () => resolve(chunks));
readStream.on('error', reject);
});
}
// ✅ BEST: pipeline for chaining transformations
const { pipeline } = require('stream/promises');
const zlib = require('zlib');
async function compressFile(input, output) {
await pipeline(
fs.createReadStream(input), // Source
zlib.createGzip(), // Transform
fs.createWriteStream(output) // Destination
);
// Automatic error handling and backpressure management
}ควรใช้ Streams เมื่อใดก็ตามที่ขนาดข้อมูลอาจเกินหลาย MB หรือสำหรับการประมวลผลแบบ real-time (อัปโหลด, ล็อก, ข้อมูลเครือข่าย)
คำถามที่ 7: อธิบายแนวคิดของ Backpressure
Backpressure เกิดขึ้นเมื่อผู้ผลิตข้อมูลเร็วกว่าผู้บริโภค หากไม่มีการจัดการ หน่วยความจำจะล้น
const fs = require('fs');
// ❌ Problem: no backpressure handling
function badCopy(src, dest) {
const readable = fs.createReadStream(src);
const writable = fs.createWriteStream(dest);
readable.on('data', (chunk) => {
// If write() returns false, the internal buffer is full
// But here reading continues anyway → memory leak
writable.write(chunk);
});
}
// ✅ Solution: respect the writable signal
function goodCopy(src, dest) {
const readable = fs.createReadStream(src);
const writable = fs.createWriteStream(dest);
readable.on('data', (chunk) => {
const canContinue = writable.write(chunk);
if (!canContinue) {
// Pause reading until buffer drains
readable.pause();
}
});
writable.on('drain', () => {
// Buffer drained, resume reading
readable.resume();
});
readable.on('end', () => writable.end());
}
// ✅ BEST: pipe() handles everything automatically
function bestCopy(src, dest) {
const readable = fs.createReadStream(src);
const writable = fs.createWriteStream(dest);
// pipe() handles backpressure natively
readable.pipe(writable);
}เมธอด pipe() หรือ pipeline() จัดการ backpressure โดยอัตโนมัติ สำหรับกรณีที่ซับซ้อน จำเป็นต้องสร้างลอจิก pause/resume เอง
ประสิทธิภาพและการเพิ่มประสิทธิภาพ
คำถามที่ 8: จะระบุและแก้ไข Memory Leak ได้อย่างไร?
Memory leak เป็นปัญหาที่พบบ่อยใน Node.js การรู้วิธีตรวจจับและแก้ไขเป็นสิ่งจำเป็นในสภาพแวดล้อม production
// ❌ Leak 1: closures that retain references
function createLeakyHandler() {
const hugeData = Buffer.alloc(100 * 1024 * 1024); // 100MB
return function handler(req, res) {
// hugeData remains in memory as long as handler exists
res.end('Hello');
};
}
// ✅ Fix: limit the scope
function createSafeHandler() {
return function handler(req, res) {
// Data created and released on each request
const data = fetchData();
res.end(data);
};
}
// ❌ Leak 2: event listeners not cleaned up
class LeakyClass {
constructor() {
// Added on each instantiation, never removed
process.on('message', this.handleMessage);
}
handleMessage(msg) { /* ... */ }
}
// ✅ Fix: explicit cleanup
class SafeClass {
constructor() {
this.boundHandler = this.handleMessage.bind(this);
process.on('message', this.boundHandler);
}
handleMessage(msg) { /* ... */ }
destroy() {
// Mandatory cleanup
process.removeListener('message', this.boundHandler);
}
}
// Diagnostics with native tools
function diagnoseMemory() {
const used = process.memoryUsage();
console.log({
heapUsed: `${Math.round(used.heapUsed / 1024 / 1024)}MB`,
heapTotal: `${Math.round(used.heapTotal / 1024 / 1024)}MB`,
external: `${Math.round(used.external / 1024 / 1024)}MB`,
rss: `${Math.round(used.rss / 1024 / 1024)}MB`,
});
}
// Enable manual garbage collector for testing
// node --expose-gc app.js
if (global.gc) {
global.gc();
diagnoseMemory();
}ในสภาพแวดล้อม production ควรใช้เครื่องมืออย่าง clinic.js, heap snapshot จาก Chrome DevTools หรือโซลูชัน APM (Application Performance Monitoring) เช่น DataDog หรือ New Relic
คำถามที่ 9: จะเพิ่มประสิทธิภาพ API Node.js ได้อย่างไร?
คำถามนี้ประเมินความรู้เกี่ยวกับเทคนิคการเพิ่มประสิทธิภาพในหลายระดับ
// 1. CACHING: reduce expensive calls
const NodeCache = require('node-cache');
const cache = new NodeCache({ stdTTL: 300 }); // 5-minute TTL
async function getCachedUser(id) {
const cacheKey = `user:${id}`;
let user = cache.get(cacheKey);
if (!user) {
user = await db.users.findById(id);
cache.set(cacheKey, user);
}
return user;
}
// 2. CONNECTION POOLING: reuse DB connections
const { Pool } = require('pg');
const pool = new Pool({
max: 20, // Max simultaneous connections
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
});
// 3. COMPRESSION: reduce response size
const compression = require('compression');
app.use(compression({
filter: (req, res) => {
// Only compress if > 1KB
return compression.filter(req, res);
},
threshold: 1024,
}));
// 4. BATCHING: group operations
async function batchInsert(items) {
const BATCH_SIZE = 1000;
for (let i = 0; i < items.length; i += BATCH_SIZE) {
const batch = items.slice(i, i + BATCH_SIZE);
await db.items.insertMany(batch);
}
}
// 5. LAZY LOADING: load on demand
async function getUserWithPosts(userId, includePosts = false) {
const user = await db.users.findById(userId);
if (includePosts) {
user.posts = await db.posts.findByUserId(userId);
}
return user;
}การเพิ่มประสิทธิภาพควรได้รับการชี้นำด้วย profiling ควรวัดผลก่อนเพิ่มประสิทธิภาพเพื่อระบุ bottleneck ที่แท้จริง
80% ของปัญหาด้านประสิทธิภาพมาจาก 20% ของโค้ด ควรใช้ profiling เพื่อระบุพื้นที่วิกฤตเหล่านี้ก่อนที่จะเพิ่มประสิทธิภาพแบบสุ่ม
ความปลอดภัย
คำถามที่ 10: จะปกป้อง API Node.js จากการโจมตีทั่วไปได้อย่างไร?
ความปลอดภัยเป็นหัวข้อสัมภาษณ์ที่พบบ่อย การแสดงความรู้เกี่ยวกับช่องโหว่ OWASP เป็นสิ่งที่คาดหวัง
const express = require('express');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const mongoSanitize = require('express-mongo-sanitize');
const xss = require('xss-clean');
const app = express();
// 1. SECURITY HEADERS with Helmet
app.use(helmet());
// 2. RATE LIMITING against brute-force attacks
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per IP
message: 'Too many requests, please try again later',
standardHeaders: true,
legacyHeaders: false,
});
app.use('/api/', limiter);
// 3. SANITIZATION against NoSQL injections
app.use(mongoSanitize());
// 4. XSS PROTECTION
app.use(xss());
// 5. STRICT INPUT VALIDATION
const { body, validationResult } = require('express-validator');
app.post('/api/users',
[
body('email').isEmail().normalizeEmail(),
body('password').isLength({ min: 8 }).escape(),
body('name').trim().escape(),
],
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// Continue processing
}
);
// 6. SQL INJECTION PROTECTION (with parameters)
async function safeQuery(userId) {
// ✅ Parameterized query
const result = await pool.query(
'SELECT * FROM users WHERE id = $1',
[userId]
);
return result.rows;
}
// ❌ NEVER string concatenation
async function unsafeQuery(userId) {
// Vulnerable to SQL injection
const result = await pool.query(
`SELECT * FROM users WHERE id = ${userId}`
);
}ในสภาพแวดล้อม production ควรเพิ่ม: CORS ที่เข้มงวด, HTTPS บังคับ, การบันทึกความปลอดภัย, การหมุนเวียน secret และการตรวจสอบ dependency เป็นประจำ (npm audit)
สถาปัตยกรรมและรูปแบบการออกแบบ
คำถามที่ 11: อธิบาย Repository Pattern ใน Node.js
Repository Pattern แยกการเข้าถึงข้อมูลออกจากกันและช่วยให้การทดสอบและการบำรุงรักษาง่ายขึ้น
// Abstract interface (for TypeScript, or documentation)
class UserRepository {
async findById(id) { throw new Error('Not implemented'); }
async findByEmail(email) { throw new Error('Not implemented'); }
async create(userData) { throw new Error('Not implemented'); }
async update(id, userData) { throw new Error('Not implemented'); }
async delete(id) { throw new Error('Not implemented'); }
}
// Concrete implementation with Prisma
class PrismaUserRepository extends UserRepository {
constructor(prisma) {
super();
this.prisma = prisma;
}
async findById(id) {
return this.prisma.user.findUnique({ where: { id } });
}
async findByEmail(email) {
return this.prisma.user.findUnique({ where: { email } });
}
async create(userData) {
return this.prisma.user.create({ data: userData });
}
async update(id, userData) {
return this.prisma.user.update({
where: { id },
data: userData,
});
}
async delete(id) {
return this.prisma.user.delete({ where: { id } });
}
}
// Implementation for testing
class InMemoryUserRepository extends UserRepository {
constructor() {
super();
this.users = new Map();
this.idCounter = 1;
}
async findById(id) {
return this.users.get(id) || null;
}
async create(userData) {
const user = { id: this.idCounter++, ...userData };
this.users.set(user.id, user);
return user;
}
// ... other methods
}
// Service using the repository (dependency injection)
class UserService {
constructor(userRepository) {
this.userRepository = userRepository;
}
async getUser(id) {
const user = await this.userRepository.findById(id);
if (!user) throw new Error('User not found');
return user;
}
}รูปแบบนี้ช่วยให้เปลี่ยนการ implement การจัดเก็บข้อมูลได้โดยไม่ต้องแก้ไข business logic
คำถามที่ 12: จะ implement ระบบ Job Queue ได้อย่างไร?
คิวช่วยให้สามารถเลื่อนงานหนักและรับประกันการทำงานที่เชื่อถือได้
const Queue = require('bull');
// Create queue with Redis as backend
const emailQueue = new Queue('email', {
redis: {
host: 'localhost',
port: 6379,
},
defaultJobOptions: {
attempts: 3, // Number of attempts
backoff: {
type: 'exponential',
delay: 2000, // Initial delay between attempts
},
removeOnComplete: 100, // Keep last 100 completed jobs
},
});
// Producer: add jobs to the queue
async function sendWelcomeEmail(userId, email) {
await emailQueue.add('welcome', {
userId,
email,
template: 'welcome',
}, {
priority: 1, // High priority
delay: 5000, // 5-second delay
});
}
// Consumer: process jobs
emailQueue.process('welcome', async (job) => {
const { userId, email, template } = job.data;
// Update progress
job.progress(10);
const html = await renderTemplate(template, { userId });
job.progress(50);
await sendEmail(email, 'Welcome!', html);
job.progress(100);
return { sent: true, email };
});
// Event handling
emailQueue.on('completed', (job, result) => {
console.log(`Job ${job.id} completed:`, result);
});
emailQueue.on('failed', (job, err) => {
console.error(`Job ${job.id} failed:`, err.message);
});
// Recurring jobs (cron)
emailQueue.add('newsletter', { type: 'weekly' }, {
repeat: {
cron: '0 9 * * MON', // Every Monday at 9am
},
});Bull กับ Redis เป็นโซลูชันที่ได้รับความนิยมมากที่สุด สำหรับความต้องการที่เรียบง่ายกว่า agenda หรือ bee-queue เป็นทางเลือกที่เบากว่า
คำถามขั้นสูง
คำถามที่ 13: โมดูล Native N-API ทำงานอย่างไร?
N-API ช่วยให้สร้างโมดูล native ด้วย C/C++ โดยมี API ที่เสถียรข้ามเวอร์ชัน Node.js
// Native module for CPU-intensive calculations
#include <napi.h>
// Synchronous function exposed to JavaScript
Napi::Number Fibonacci(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
// Argument validation
if (info.Length() < 1 || !info[0].IsNumber()) {
Napi::TypeError::New(env, "Number expected")
.ThrowAsJavaScriptException();
return Napi::Number::New(env, 0);
}
int n = info[0].As<Napi::Number>().Int32Value();
// Iterative Fibonacci calculation
long long a = 0, b = 1;
for (int i = 0; i < n; i++) {
long long temp = a + b;
a = b;
b = temp;
}
return Napi::Number::New(env, static_cast<double>(a));
}
// Module initialization
Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports.Set(
Napi::String::New(env, "fibonacci"),
Napi::Function::New(env, Fibonacci)
);
return exports;
}
NODE_API_MODULE(native_module, Init)// Usage from JavaScript
const native = require('./build/Release/native_module');
// 10x faster than JavaScript equivalent
const result = native.fibonacci(50);โมดูล native มีประโยชน์สำหรับการคำนวณเชิงลึก การรวม library C/C++ ที่มีอยู่ หรือการเข้าถึง API ระบบ
คำถามที่ 14: อธิบาย V8 Garbage Collector
การเข้าใจ GC ช่วยในการเขียนโค้ดที่ลดการหยุดชั่วคราวและการใช้หน่วยความจำ
// V8 GC uses two spaces: Young and Old Generation
// 1. Young Generation: short-lived objects
function shortLivedObjects() {
for (let i = 0; i < 1000; i++) {
const temp = { data: i }; // Allocated then collected quickly
}
// Minor GC (Scavenge) very fast
}
// 2. Old Generation: objects that survive multiple GCs
const cache = new Map(); // Survives, promoted to Old Generation
// ❌ Problematic pattern: many promoted objects
function createManyLongLived() {
const objects = [];
for (let i = 0; i < 100000; i++) {
objects.push({ id: i, data: new Array(100).fill(0) });
}
return objects; // All promoted to Old Gen = slow major GC
}
// ✅ Optimized pattern: object reuse
class ObjectPool {
constructor(factory, size = 100) {
this.pool = Array.from({ length: size }, factory);
this.available = [...this.pool];
}
acquire() {
return this.available.pop() || this.pool[0];
}
release(obj) {
// Reset and return to pool
Object.keys(obj).forEach(k => obj[k] = null);
this.available.push(obj);
}
}
// GC monitoring
const v8 = require('v8');
function getHeapStats() {
const stats = v8.getHeapStatistics();
return {
totalHeap: `${Math.round(stats.total_heap_size / 1024 / 1024)}MB`,
usedHeap: `${Math.round(stats.used_heap_size / 1024 / 1024)}MB`,
heapLimit: `${Math.round(stats.heap_size_limit / 1024 / 1024)}MB`,
};
}flag --max-old-space-size ช่วยให้เพิ่มขีดจำกัดของ Old Generation สำหรับแอปพลิเคชันที่ใช้หน่วยความจำมาก
คำถามที่ 15: จะ implement Graceful Shutdown ได้อย่างไร?
Graceful shutdown ช่วยให้สามารถจัดการ request ที่กำลังดำเนินอยู่ให้เสร็จและปิดการเชื่อมต่ออย่างถูกต้องก่อนหยุด server
const http = require('http');
const server = http.createServer((req, res) => {
// Simulate a long request
setTimeout(() => {
res.writeHead(200);
res.end('Done');
}, 2000);
});
// Tracking active connections
let connections = new Set();
server.on('connection', (conn) => {
connections.add(conn);
conn.on('close', () => connections.delete(conn));
});
// Graceful shutdown function
async function shutdown(signal) {
console.log(`${signal} received, starting graceful shutdown...`);
// 1. Stop accepting new connections
server.close(() => {
console.log('HTTP server closed');
});
// 2. Close idle connections
for (const conn of connections) {
conn.end();
}
// 3. Close DB connections, queues, etc.
await Promise.all([
database.disconnect(),
redisClient.quit(),
messageQueue.close(),
]);
// 4. Safety timeout
setTimeout(() => {
console.error('Forced shutdown after timeout');
process.exit(1);
}, 30000);
console.log('Graceful shutdown completed');
process.exit(0);
}
// Listen for termination signals
process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));
// Start server
server.listen(3000, () => {
console.log('Server running on port 3000');
});ในสภาพแวดล้อม production ที่ใช้ container (Docker, Kubernetes) graceful shutdown มีความสำคัญอย่างยิ่งสำหรับการ deploy แบบ zero-downtime
พร้อมที่จะพิชิตการสัมภาษณ์ Node.js / NestJS แล้วหรือยังครับ?
ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ
คำถามเชิงพฤติกรรม
คำถามที่ 16: อธิบายปัญหาด้านประสิทธิภาพที่เคยแก้ไข
คำถามนี้ประเมินประสบการณ์จริง คำตอบควรจัดโครงสร้างตามรูปแบบ STAR (Situation, Task, Action, Result)
ตัวอย่างคำตอบที่มีโครงสร้าง:
Situation: A reporting API was timing out on requests
exceeding 100,000 records.
Task: Reduce response time from 45s to under 5s.
Action:
1. Profiling with clinic.js → identified JSON serialization as bottleneck
2. Implemented streaming with Transform streams
3. Database-side pagination
4. Added Redis caching for frequent queries
Result: Response time reduced to 2s, memory usage decreased by 10x.คำถามที่ 17: จัดการ dependency และการอัปเดตอย่างไร?
{
"dependencies": {
// ✅ Exact versions for production
"express": "4.18.2",
// ✅ Caret for compatible minor updates
"lodash": "^4.17.21",
// ❌ Avoid latest or *
// "some-lib": "*"
},
"devDependencies": {
// Quality tools
"npm-check-updates": "^16.0.0"
},
"scripts": {
// Vulnerability check
"audit": "npm audit --audit-level=moderate",
// Interactive update
"update:check": "ncu",
"update:apply": "ncu -u && npm install"
},
"engines": {
// Specify required Node.js version
"node": ">=20.0.0"
}
}ควรกล่าวถึงการใช้ package-lock.json, Dependabot หรือ Renovate สำหรับระบบอัตโนมัติ และการทดสอบ regression ก่อนการอัปเดตหลักทุกครั้ง
บทสรุป
การสัมภาษณ์ Backend Node.js ประเมินทั้งความเข้าใจทางทฤษฎีเกี่ยวกับกลไกภายในและความสามารถในการแก้ปัญหา production จริง การเชี่ยวชาญ Event Loop, รูปแบบ asynchronous และเทคนิคการเพิ่มประสิทธิภาพเป็นพื้นฐานที่คาดหวังสำหรับตำแหน่ง backend developer ระดับอาวุโส
รายการตรวจสอบการเตรียมตัว
- ✅ เข้าใจการทำงานของ Event Loop และเฟสต่างๆ
- ✅ เชี่ยวชาญความแตกต่างระหว่าง callback, Promise และ async/await
- ✅ รู้จักรูปแบบการจัดการ error แบบ asynchronous
- ✅ รู้ว่าเมื่อใดควรใช้ Streams เทียบกับวิธีแบบดั้งเดิม
- ✅ ระบุและแก้ไข memory leak ได้
- ✅ นำแนวปฏิบัติความปลอดภัย OWASP ที่ดีที่สุดไปใช้
- ✅ implement clustering และ graceful shutdown
- ✅ ใช้เครื่องมือ profiling (clinic.js, Chrome DevTools)
เริ่มฝึกซ้อมเลย!
ทดสอบความรู้ของคุณด้วยตัวจำลองสัมภาษณ์และแบบทดสอบเทคนิคครับ
การเตรียมตัวด้านเทคนิคควรเสริมด้วยโปรเจกต์จริง การสร้าง API สำหรับ production, การมีส่วนร่วมในโปรเจกต์ open source Node.js หรือการแก้โจทย์บนแพลตฟอร์มอย่าง LeetCode ช่วยเสริมสร้างความรู้เหล่านี้ให้แข็งแกร่ง
แท็ก
แชร์
บทความที่เกี่ยวข้อง

NestJS + Prisma: สแตกแบ็กเอนด์สมัยใหม่สำหรับ Node.js
คู่มือฉบับสมบูรณ์ในการสร้าง API แบ็กเอนด์สมัยใหม่ด้วย NestJS และ Prisma ครอบคลุมการตั้งค่า โมเดล เซอร์วิส ทรานแซกชัน และแนวปฏิบัติที่ดี

NestJS: สร้าง REST API ที่สมบูรณ์ตั้งแต่เริ่มต้น
คู่มือฉบับสมบูรณ์สำหรับการสร้าง REST API ระดับมืออาชีพด้วย NestJS ครอบคลุม Controller, Service, Module, การตรวจสอบข้อมูลด้วย class-validator และการจัดการข้อผิดพลาด

Microservices ด้วย NestJS ปี 2026: สถาปัตยกรรม, gRPC และคำถามสัมภาษณ์งาน
คู่มือฉบับสมบูรณ์เรื่องสถาปัตยกรรม NestJS Microservices กับ gRPC: transport layer, Protocol Buffers, streaming patterns และคำถามสัมภาษณ์งานสำหรับ backend engineer ปี 2026