Một đội ngũ frontend đã triển khai một lớp API-mocking type-safe chỉ hoạt động trong môi trường phát triển. Nhờ sử dụng Axios interceptors và tính năng tree-shaking của Vite, bản build production vẫn không bị ảnh hưởng. Các kỹ sư có thể lấy dữ liệu bằng các pattern quen thuộc trong khi chờ đợi các endpoint từ backend, sau đó chỉ cần chuyển một cờ môi trường (environment flag) duy nhất để kết nối với API thật.

Tại sao đội ngũ cần một phương pháp mock tốt hơn

Các nhà phát triển frontend thường gặp bế tắc khi một route backend chưa hoàn thiện. Cách xử lý nhanh — như hard-code phản hồi ngay trong component hoặc rải rác các khối if (process.env.NODE_ENV === 'development') khắp UI — giúp ứng dụng tiếp tục chạy nhưng lại để lại nợ kỹ thuật (technical debt). Những đối tượng mock này dần trở thành một phần logic của component, làm tăng rủi ro đưa dữ liệu giả lên môi trường production và khiến mã nguồn trở nên khó đọc, khó kiểm thử hơn.

Đội ngũ muốn đưa mọi bản mock ra khỏi cây component, thiết lập một hợp đồng (contract) giữa frontend và backend, đồng thời đảm bảo không có gì dư thừa lọt vào bản build production.

Quy trình ba bước mà đội ngũ thực hiện

  1. Họp thống nhất hợp đồng (Contract meeting) – Các kỹ sư frontend và backend ngồi lại với nhau để liệt kê từng request, bao gồm URL, method và payload dự kiến.
  2. Hợp đồng có kiểu (Typed contract) – Họ chuyển danh sách đó thành một TypeScript interface, đóng vai trò là nguồn sự thật duy nhất (single source of truth) cho cấu trúc request và response.
  3. Kết nối interceptor (Interceptor wiring) – Một Axios interceptor sẽ kiểm tra mọi request gửi đi. Nếu URL khớp với một bản mock đã đăng ký, interceptor sẽ trả về dữ liệu mock; nếu không, request sẽ được gửi đến server thật.

Vì interceptor là nơi duy nhất chứa logic mock, mã nguồn của component vẫn được giữ nguyên. Các nhà phát triển tiếp tục sử dụng các data-fetching hooks thông thường — chẳng hạn như useQuery — mà không cần thêm bất kỳ logic điều kiện nào.

Cách tránh làm phình to bản build production

Đội ngũ đã thiết lập ba lớp bảo vệ để Rollup (trình đóng gói được Vite sử dụng) có thể loại bỏ hoàn toàn mã mock khi build cho production:

  • import.meta.env.DEV sẽ trả về false trong bản build production, do đó toàn bộ module interceptor sẽ biến mất trong quá trình tree-shaking.
  • Biến MODE được thiết lập thành một giá trị khác với test khi chạy unit test, giúp tách biệt mã chỉ dành cho việc kiểm thử.
  • Một cờ tùy chỉnh, VITE_ENABLE_MSW, mặc định là false và chỉ được bật một cách rõ ràng để kích hoạt mock.

Khi cả ba điều kiện trên đều là false, registry của các bản mock sẽ không bao giờ xuất hiện trong bản bundle cuối cùng.

Tổ chức các tệp mock

Repository tuân theo bố cục tập trung vào tính năng (feature-centric):

  • interfaces/ – Chứa các định nghĩa TypeScript được tạo ra từ buổi họp thống nhất hợp đồng.
  • scenarios.ts – Chứa các ví dụ cụ thể về phản hồi thành công và các trường hợp lỗi cho mỗi endpoint.
  • devHandlers.ts – Đóng vai trò là registry trung tâm để ánh xạ các URL với dữ liệu scenario và kết nối interceptor vào Axios.

Một script scaffolding nhỏ có thể tự động tạo các tệp này: chỉ cần cung cấp URL và interface tương ứng, nó sẽ tạo ra các tệp stub và đăng ký bản mock. Script này nằm ngoài luồng mã nguồn production, vì vậy nó không ảnh hưởng đến kích thước bundle.

Những gì đội ngũ đã đạt được

  • Không còn mock bên trong các component – Tất cả dữ liệu giả đều nằm trong một lớp riêng biệt, giúp mã UI luôn sạch sẽ.
  • An toàn kiểu đầu cuối (End-to-end type safety) – Dữ liệu mock tuân thủ cùng một TypeScript interface được sử dụng cho các phản hồi thật, nhờ đó các lỗi không khớp sẽ được phát hiện ngay tại thời điểm biên dịch.
  • Không làm nặng bản production – Tree-shaking loại bỏ hoàn toàn interceptor và dữ liệu mock, giữ cho kích thước bundle không thay đổi.
  • Dùng chung kịch bản cho dev và test – Cùng một định nghĩa mock được sử dụng cho cả phát triển local và kiểm thử tự động, giúp giảm thiểu sự trùng lặp.

Những đánh đổi và hạn chế

Cách tiếp cận này không thay thế cho một backend thực sự. Nếu hợp đồng mock bị sai lệch so với API thực tế, các nhà phát triển sẽ chỉ phát hiện ra sự sai lệch đó sau khi họ chuyển sang cờ môi trường thật.

Những điều cần lưu ý tiếp theo

  • Tích hợp công cụ (Tooling integration)
  • Áp dụng rộng rãi hơn (Broader adoption)
  • Giám sát hiệu năng (Performance monitoring)

Bài học rút ra rất rõ ràng: việc đưa logic mock vào một lớp được kiểm soát bởi môi trường và có kiểu dữ liệu chặt chẽ cho phép các đội ngũ frontend giữ cho các component luôn nguyên bản, đảm bảo an toàn về kiểu và triển khai các bản build production mà không chứa các payload mock ẩn. Duy trì một hợp đồng chung chính là cái giá để có được một quy trình phát triển mượt mà hơn và một mã nguồn sạch sẽ hơn.