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

25 คำถามสัมภาษณ์งาน Laravel และ PHP ยอดนิยมในปี 2026
รวม 25 คำถามสัมภาษณ์งาน Laravel และ PHP ที่พบบ่อยที่สุด ครอบคลุม Service Container, Eloquent ORM, middleware, queues และการ deploy ระบบ production พร้อมคำตอบและตัวอย่างโค้ดครบถ้วน

คำถามสัมภาษณ์งาน Django และ Python: 25 คำถามยอดนิยมประจำปี 2026
25 คำถามสัมภาษณ์งาน Django และ Python ที่พบบ่อยที่สุด ORM, views, middleware, DRF, signals และการปรับแต่งประสิทธิภาพ พร้อมคำตอบละเอียดและตัวอย่างโค้ด