Việc làm

07/10 · Hà Nội · Tại văn phòng

Fullstack Developer (NodeJS, NestJS, Angular, MongoDB)

IONAH · Fullstack Developer

Xem mô tả & ứng tuyển trên ITviecMô tả công việc, lương và ứng tuyển nằm ở ITviec.

10 câu hỏi có thể gặp khi phỏng vấn

Chọn theo kỹ năng và cấp của tin (Node.js, JavaScript & TypeScript, SQL & Database). Gợi ý trả lời do Drama Công Ty biên soạn để bạn ôn nhanh, không phải đề của công ty.

  1. 01Bạn mở rộng (scale) một API Node.js thế nào? Graceful shutdown là gì và vì sao cần?Node.js · Senior

    Gợi ý trả lời

    Node một process chỉ dùng tốt một core, nên scale theo chiều ngang:

    • Chạy nhiều process (cluster, PM2) hoặc nhiều container sau load balancer, Kubernetes tự tăng giảm số pod.
    • App phải stateless: session, cache, rate limit, lock để ở Redis hoặc DB, không để trong bộ nhớ process.
    • WebSocket nhiều instance cần adapter qua Redis pub/sub để gửi tin giữa các instance.
    • Đặt giới hạn connection pool DB theo tổng số instance.

    Graceful shutdown: khi nhận SIGTERM (deploy, scale down), ngừng nhận request mới, chờ request đang chạy xong, đóng DB và queue, rồi thoát:

    process.on("SIGTERM", () => {
      server.close(async () => {
        await db.close();
        process.exit(0);
      });
    });
    

    Hay sai: không xử lý SIGTERM, mỗi lần deploy lại cắt ngang request và job đang làm dở. Nên đặt thêm timeout buộc thoát nếu chờ quá lâu.

    Ôn thêm câu hỏi Node.js →
  2. 02Khi xây dựng API bằng Node.js, bạn chú ý những vấn đề bảo mật nào?Node.js · Senior

    Gợi ý trả lời

    • Validate mọi input ở biên bằng schema (Zod, Joi, class-validator trong NestJS); chỉ nhận field cho phép để tránh mass assignment.
    • Injection: dùng query có tham số hoặc ORM, không nối chuỗi SQL. Với MongoDB, chặn object toán tử như { "$gt": "" } lọt vào điều kiện truy vấn.
    • Xác thực và phân quyền: kiểm tra quyền trên từng tài nguyên (user này có sở hữu đơn hàng này không), không chỉ kiểm tra đã đăng nhập.
    • Rate limiting cho login, OTP, API tốn tài nguyên.
    • Header bảo mật (Helmet), CORS chỉ mở cho origin cần thiết.
    • Secret để trong biến môi trường hoặc secret manager, không commit; không log token, mật khẩu.
    • Dependency: chạy npm audit hoặc công cụ quét trong CI, khoá phiên bản bằng lockfile.

    Người phỏng vấn muốn nghe: lỗi phân quyền theo đối tượng (IDOR) rất phổ biến, và bạn biết cách phòng nó.

    Ôn thêm câu hỏi Node.js →
  3. 03Thiết kế xử lý job nền (gửi email, đồng bộ dữ liệu) trong hệ thống Node.js thế nào để không mất job và không xử lý trùng?Node.js · Senior

    Gợi ý trả lời

    Dùng message queue thay vì xử lý ngay trong request: BullMQ (Redis), RabbitMQ hoặc dịch vụ queue của cloud. API đẩy job rồi trả về ngay; worker riêng xử lý.

    Những điểm cần thiết kế:

    • Retry có backoff (tăng dần thời gian chờ) và giới hạn số lần thử; job vẫn lỗi thì đưa vào dead-letter queue để xem lại.
    • Idempotency: hầu hết queue đảm bảo "ít nhất một lần", nên job có thể chạy hai lần. Dùng khoá idempotency hoặc kiểm tra trạng thái trước khi làm (ví dụ email đã gửi chưa).
    • Không mất job khi ghi DB: ghi dữ liệu và "sự kiện cần gửi" trong cùng transaction (outbox pattern), rồi một tiến trình khác đẩy sang queue.
    • Giới hạn concurrency của worker, timeout cho từng job.

    Hay sai: dùng setTimeout hay mảng trong bộ nhớ làm "queue". Restart hoặc deploy là mất hết job.

    Ôn thêm câu hỏi Node.js →
  4. 04Core Web Vitals gồm những chỉ số nào? Bạn cải thiện hiệu năng tải trang ra sao?JavaScript & TypeScript · Senior

    Gợi ý trả lời

    Ba chỉ số chính đo trải nghiệm thật của người dùng:

    • LCP (Largest Contentful Paint): thời gian hiện phần tử lớn nhất. Cải thiện: tối ưu ảnh (định dạng hiện đại, đúng kích thước, ưu tiên tải ảnh hero), giảm thời gian phản hồi server, cache/CDN, render phía server.
    • INP (Interaction to Next Paint, thay FID từ 2024): độ trễ khi tương tác. Cải thiện: chia nhỏ tác vụ JS dài, giảm JS chạy trên main thread, tránh render lại quá nhiều.
    • CLS (Cumulative Layout Shift): độ xô lệch bố cục. Cải thiện: khai báo kích thước cho ảnh, video, quảng cáo; tránh chèn nội dung đẩy trang xuống; xử lý font.

    Ngoài ra: code splitting và lazy load theo route, bỏ thư viện nặng không cần, nén và cache tài nguyên tĩnh.

    Người phỏng vấn muốn nghe: bạn đo bằng dữ liệu người dùng thật (field data) lẫn Lighthouse, và biết điểm Lighthouse không phải là mục tiêu cuối.

    Ôn thêm câu hỏi JavaScript & TypeScript →
  5. 05Frontend cần chú ý gì về bảo mật: XSS, CSRF, CORS? Nên lưu access token ở đâu?JavaScript & TypeScript · Senior

    Gợi ý trả lời

    • XSS: kẻ tấn công chèn script chạy trên trang của bạn. Phòng: framework tự escape khi render; tránh innerHTML, v-html, dangerouslySetInnerHTML với dữ liệu người dùng (nếu bắt buộc thì sanitize); cấu hình Content-Security-Policy.
    • CSRF: trang lạ khiến trình duyệt gửi request kèm cookie của người dùng. Phòng: cookie SameSite=Lax hoặc Strict, CSRF token, kiểm tra Origin.
    • CORS: là cơ chế trình duyệt nới lỏng same-origin policy, do server cấu hình. Nó không phải lớp bảo vệ API khỏi người gọi ngoài trình duyệt.

    Lưu token:

    • localStorage tiện nhưng script XSS nào cũng đọc được.
    • Cookie HttpOnly + Secure + SameSite thì JS không đọc được, nhưng phải tính đến CSRF.

    Người phỏng vấn muốn nghe: không có chỗ lưu nào an toàn tuyệt đối; chặn XSS mới là việc quan trọng nhất, vì có XSS thì mọi cách lưu đều bị lợi dụng.

    Ôn thêm câu hỏi JavaScript & TypeScript →
  6. 06Làm sao migrate schema (thêm cột, đổi kiểu, thêm index) trên bảng lớn mà không downtime?SQL & Database · Senior

    Gợi ý trả lời

    Chia thay đổi thành nhiều bước tương thích ngược để phiên bản code cũ và mới chạy song song được (expand rồi contract).

    1. Expand: thêm cột mới cho phép NULL hoặc có default; tránh câu lệnh viết lại cả bảng.
    2. Code mới ghi vào cả cột cũ và mới.
    3. Backfill dữ liệu cũ theo từng lô nhỏ, có nghỉ giữa các lô để không gây lag replica.
    4. Chuyển code sang đọc cột mới, kiểm tra kỹ.
    5. Contract: bỏ cột cũ ở một lần deploy sau.

    Index trên bảng lớn: PostgreSQL dùng CREATE INDEX CONCURRENTLY; MySQL dùng online DDL hoặc công cụ như gh-ost, pt-online-schema-change.

    Người phỏng vấn muốn nghe: bạn biết DDL có thể giữ lock khiến mọi query xếp hàng, nên đặt lock_timeout và luôn có kế hoạch rollback.

    Ôn thêm câu hỏi SQL & Database →
  7. 07Node.js chạy đơn luồng thì làm sao xử lý được nhiều request cùng lúc? Node hợp với loại ứng dụng nào?Node.js · Junior

    Gợi ý trả lời

    Code JavaScript của bạn chạy trên một thread, nhưng I/O (mạng, file, DB) là non-blocking: Node giao việc cho hệ điều hành hoặc thread pool của libuv, rồi tiếp tục xử lý request khác. Khi I/O xong, callback được đưa lại vào event loop.

    • Hợp với: API, BFF, real-time (WebSocket, chat), proxy, ứng dụng nhiều I/O và ít tính toán.
    • Không hợp nếu chỉ dùng thread chính cho tác vụ nặng CPU: mã hoá video, xử lý ảnh lớn, tính toán dài.
    // Chặn cả server: mọi request khác phải chờ
    app.get("/hash", (req, res) => {
      res.send(crypto.pbkdf2Sync(pw, salt, 1e6, 64, "sha512"));
    });
    

    Người phỏng vấn muốn nghe: "đơn luồng" chỉ đúng với phần code JS; Node vẫn dùng thread pool cho file system, DNS, một số hàm crypto và zlib.

    Ôn thêm câu hỏi Node.js →
  8. 08CommonJS và ES Modules khác nhau thế nào? Dự án mới nên dùng loại nào?Node.js · Junior

    Gợi ý trả lời

    CommonJS dùng require/module.exports, nạp đồng bộ lúc chạy. ES Modules dùng import/export, là chuẩn của ngôn ngữ, được phân tích tĩnh nên hỗ trợ tree-shaking và top-level await.

    • Bật ESM bằng "type": "module" trong package.json hoặc đuôi .mjs.
    • Trong ESM không có sẵn __dirname; dùng import.meta.dirname (Node mới) hoặc tính từ import.meta.url.
    • ESM import được CommonJS. Chiều ngược lại trước đây phải dùng import() động; các bản Node gần đây đã cho require() module ESM đồng bộ (trừ module có top-level await).

    Dự án mới nên dùng ESM, vì cả hệ sinh thái đang chuyển dần sang đó.

    Hay sai: khi viết ESM thuần, quên ghi đuôi file trong đường dẫn import tương đối (./utils.js), dẫn đến lỗi không tìm thấy module.

    Ôn thêm câu hỏi Node.js →
  9. 09Middleware trong Express là gì? Viết error-handling middleware thế nào?Node.js · Junior

    Gợi ý trả lời

    Middleware là hàm (req, res, next) chạy theo thứ tự đăng ký. Mỗi middleware có thể sửa req/res, trả response luôn, hoặc gọi next() để chuyển tiếp. Ví dụ: parse JSON, log, xác thực, giới hạn tần suất.

    Error-handling middleware có bốn tham số và đặt sau các route:

    app.use((err, req, res, next) => {
      logger.error(err);
      const status = err.status ?? 500;
      res.status(status).json({ message: status < 500 ? err.message : "Internal error" });
    });
    
    • Gọi next(err) để nhảy thẳng tới error handler.
    • Express 5 tự chuyển Promise bị reject trong handler async sang error handler; Express 4 thì phải tự try/catch rồi gọi next(err).

    Hay sai: quên gọi next() và cũng không trả response, khiến request treo đến khi timeout. Hoặc gửi response hai lần, gây lỗi "headers already sent".

    Ôn thêm câu hỏi Node.js →
  10. 10Vì sao bạn muốn rời công ty hiện tại?Câu hỏi hành vi & văn hoá

    Gợi ý trả lời

    Trả lời thật nhưng hướng về phía trước: nói điều bạn đang tìm kiếm, không kể xấu nơi cũ.

    Lý do chấp nhận được: muốn thử thách mới, muốn làm sản phẩm, dự án kết thúc, công ty tái cấu trúc, thu nhập chưa tương xứng (nói khéo).

    Câu mẫu: "Em đã làm 2 năm ở dự án outsourcing, học được nhiều về quy trình và làm việc với khách nước ngoài. Giờ em muốn gắn bó lâu dài với một sản phẩm để thấy được tác động của những gì mình làm, và vị trí bên mình đúng hướng đó."

    Hay sai: chê sếp, chê đồng nghiệp, kể chuyện drama. Người phỏng vấn sẽ nghĩ bạn cũng sẽ nói về công ty họ như vậy. Nếu nhảy việc nhiều, chuẩn bị lời giải thích cho từng lần.

    Ôn thêm câu hỏi Câu hỏi hành vi & văn hoá →

Bạn đã phỏng vấn ở IONAH?

Kể lại các vòng và câu hỏi đã gặp (ẩn danh) để người sau ôn đúng trọng tâm.

Chia sẻ kinh nghiệm phỏng vấn