Cách Gỡ Lỗi Socket.io? (Bao Gồm Mã Hoàn Chỉnh)

中村 拓也

中村 拓也

20 tháng 10 2025

Cách Gỡ Lỗi Socket.io? (Bao Gồm Mã Hoàn Chỉnh)

Apidog cho doanh nghiệp

Triển khai tại chỗ

SSO & RBAC

Tuân thủ SOC 2

Khám phá Apidog Enterprise

Trong thế giới ứng dụng web thời gian thực phát triển nhanh chóng, Socket.io đứng vững như một công nghệ cơ bản cho phép giao tiếp hai chiều giữa khách hàng và máy chủ. Tuy nhiên, với sức mạnh lớn đi kèm với những thách thức gỡ lỗi không thể tránh khỏi, có thể khiến ngay cả những lập trình viên dày dạn kinh nghiệm cũng phải giật tóc! 😩

Dù bạn đang xây dựng ứng dụng trò chuyện, bảng điều khiển trực tiếp hay công cụ cộng tác, việc gỡ lỗi hiệu quả là rất quan trọng để duy trì sự bình tĩnh và vận chuyển mã đáng tin cậy. Hướng dẫn toàn diện này khám phá cả khả năng gỡ lỗi tích hợp của Socket.io và giới thiệu công cụ gỡ lỗi Socket.io của Apidog đang thay đổi cuộc chơi cho các lập trình viên ở khắp mọi nơi.

Hãy cùng khám phá và biến những cơn ác mộng gỡ lỗi thành những chuyến đi suôn sẻ! 🚀

Hiểu Về Các Khả Năng Gỡ Lỗi Tích Hợp Của Socket.io

Socket.io được trang bị những khả năng gỡ lỗi mạnh mẽ nhưng thường bị bỏ qua, có thể tiết kiệm hàng giờ khắc phục sự cố. Tại cốt lõi, Socket.io tận dụng mô-đun debug tối giản nhưng mạnh mẽ do TJ Holowaychuk tạo ra.

Trước phiên bản Socket.io 1.0, máy chủ sẽ xuất mọi thứ ra console theo mặc định—hữu ích cho một số người, nhưng quá dài dòng cho nhiều người khác. Cách tiếp cận hiện tại sang trọng hơn nhiều: hoàn toàn im lặng theo mặc định, với việc gỡ lỗi theo tùy chọn thông qua các biến môi trường hoặc các thuộc tính localStorage.

Khái niệm cơ bản rất đơn giản: mỗi mô-đun Socket.io cung cấp các phạm vi gỡ lỗi khác nhau, cung cấp cái nhìn sâu sắc về hoạt động bên trong của nó. Các lập trình viên có thể chọn lọc kích hoạt các phạm vi này để nhận được thông tin chính xác mà họ cần mà không bị lạc trong các nhật ký không liên quan.

Kích Hoạt Gỡ Lỗi Socket.io Trong Các Ứng Dụng Node.js

Để kích hoạt gỡ lỗi trong môi trường Node.js, các lập trình viên có thể sử dụng biến môi trường DEBUG. Cú pháp đơn giản nhưng linh hoạt:

# Kích hoạt tất cả đầu ra gỡ lỗi
DEBUG=* node yourfile.js

# Tập trung chỉ vào các tin nhắn liên quan đến khách hàng Socket.io
DEBUG=socket.io:client* node yourfile.js

# Xem cả tin nhắn Engine.IO và Socket.io
DEBUG=engine,socket.io* node yourfile.js

Cách tiếp cận này cung cấp điều khiển chi tiết về thông tin xuất hiện trong console. Đối với các ứng dụng phức tạp với nhiều kết nối Socket.io, khả năng lọc này trở nên vô giá—cho phép các lập trình viên tập trung vào các thành phần cụ thể mà không có tiếng ồn từ các phần không liên quan của hệ thống.

Triển Khai Gỡ Lỗi Socket.io Bên Khách Trong Trình Duyệt

Đối với việc gỡ lỗi phía khách trong các trình duyệt, cơ chế hoạt động tương tự nhưng sử dụng localStorage thay vì các biến môi trường:

// Kích hoạt tất cả gỡ lỗi
localStorage.debug = '*';

// Tập trung vào các thành phần cụ thể của Socket.io
localStorage.debug = 'socket.io:client*';

// Xóa tất cả cài đặt gỡ lỗi
localStorage.debug = '';

Sau khi thiết lập các giá trị này, làm mới trang sẽ kích hoạt đầu ra gỡ lỗi được chỉ định trong console của trình duyệt. Điều này chứng minh hữu ích đặc biệt khi khắc phục các vấn đề kết nối hoặc điều tra các vấn đề xử lý sự kiện trong ứng dụng khách.

Tạo Middleware Gỡ Lỗi Tùy Chỉnh Cho Socket.io

Đối với các nhu cầu gỡ lỗi nâng cao hơn, các lập trình viên thường triển khai middleware tùy chỉnh để chặn và ghi lại các sự kiện Socket.io. Cách tiếp cận này cung cấp tính linh hoạt lớn hơn và có thể được điều chỉnh để đáp ứng các yêu cầu cụ thể của ứng dụng:

//Middleware gỡ lỗi tùy chỉnh phía máy chủ
io.use((socket, next) => {
  // Ghi lại tất cả các sự kiện đến
  const originalOnEvent = socket.onevent;
  socket.onevent = function(packet) {
    const args = packet.data || [];
    console.log(`[${new Date().toISOString()}] INCOMING [${socket.id}]: ${args[0]}`, 
      JSON.stringify(args.slice(1)));
    originalOnEvent.call(this, packet);
  };
  
  // Ghi lại tất cả các sự kiện đi
  const originalEmit = socket.emit;
  socket.emit = function(event, ...args) {
    if (event !== 'newListener') {  // Lọc các sự kiện nội bộ
      console.log(`[${new Date().toISOString()}] OUTGOING [${socket.id}]: ${event}`, 
        JSON.stringify(args));
    }
    return originalEmit.apply(this, [event, ...args]);
  };
  
  next();
});

Cách tiếp cận middleware này mang lại một số lợi thế:

Bằng cách triển khai middleware như vậy, các nhóm phát triển có thể có tầm nhìn toàn diện về luồng sự kiện trong ứng dụng Socket.io của họ, khiến việc xác định và giải quyết các vấn đề dễ dàng hơn nhiều.

Các Kỹ Thuật Gỡ Lỗi Socket.io Nâng Cao Sử Dụng Mã

Vượt ra ngoài việc ghi nhật ký cơ bản, các lập trình viên có kinh nghiệm áp dụng nhiều kỹ thuật tinh vi để gỡ lỗi hiệu quả các ứng dụng Socket.io. Những cách tiếp cận này tận dụng cả khả năng bên trong của Socket.io và các công cụ bên ngoài để cung cấp cái nhìn sâu sắc hơn về hành vi của ứng dụng.

Xác Nhận Sự Kiện Để Kiểm Tra

Cơ chế xác nhận của Socket.io phục vụ như một công cụ gỡ lỗi tuyệt vời. Bằng cách sử dụng các callback với các sự kiện phát đi, các lập trình viên có thể xác nhận rằng các tin nhắn đã được nhận và xử lý đúng cách:

// Bên khách với xác nhận
socket.emit('update-profile', { name: 'Alex' }, (response) => {
  console.log('Máy chủ đã xác nhận cập nhật hồ sơ:', response);
  if (response.error) {
    console.error('Lỗi cập nhật hồ sơ:', response.error);
  }
});

// Bên máy chủ xử lý với xác nhận
socket.on('update-profile', (data, callback) => {
  try {
    // Xử lý cập nhật hồ sơ
    updateUserProfile(socket.userId, data);
    callback({ success: true });
  } catch (error) {
    console.error('Lỗi cập nhật hồ sơ:', error);
    callback({ error: error.message });
  }
});

Hình thức này tạo ra một vòng phản hồi kín, cho thấy ngay lập tức khi các tin nhắn không được xử lý như mong đợi. Sự xác nhận phục vụ như một công cụ gỡ lỗi trong quá trình phát triển và một cơ chế độ tin cậy trong sản xuất.

Tạo Bảng Điều Khiển Giám Sát Socket.io

Đối với các ứng dụng có yêu cầu thời gian thực phức tạp, các lập trình viên đôi khi tạo các bảng điều khiển giám sát chuyên dụng để trực quan hóa các kết nối và sự kiện Socket.io:

// Điểm cuối giám sát phía máy chủ
app.get('/socket-monitor', (req, res) => {
  const connectedSockets = Object.keys(io.sockets.sockets).length;
  const roomSizes = {};
  
  // Thu thập thông tin về phòng
  for (const [roomName, room] of io.sockets.adapter.rooms.entries()) {
    if (!roomName.match(/^[^/]/)) {  // Lọc ra ID socket
      roomSizes[roomName] = room.size;
    }
  }
  
  // Trả về dữ liệu giám sát
  res.json({
    connections: {
      current: connectedSockets,
      peak: global.peakConnections || connectedSockets
    },
    rooms: roomSizes,
    uptime: process.uptime()
  });
});

// Theo dõi kết nối đỉnh
io.on('connection', (socket) => {
  const currentConnections = Object.keys(io.sockets.sockets).length;
  global.peakConnections = Math.max(global.peakConnections || 0, currentConnections);
  // Xử lý kết nối khác
});

Các bảng điều khiển như vậy cung cấp cái nhìn thời gian thực giá trị về sức khỏe và mẫu sử dụng của ứng dụng, khiến việc xác định các vấn đề như rò rỉ kết nối hoặc phát triển phòng không mong đợi dễ dàng hơn.

Phát Lại Sự Kiện Socket.io Để Kiểm Tra

Một kỹ thuật gỡ lỗi mạnh mẽ khác bao gồm việc ghi lại và phát lại các sự kiện Socket.io để tái tạo và chẩn đoán các vấn đề:

// Ghi lại các sự kiện để phát lại
const eventLog = [];
io.on('connection', (socket) => {
  // Ghi lại các sự kiện đến
  socket.onAny((event, ...args) => {
    eventLog.push({
      timestamp: Date.now(),
      socketId: socket.id,
      direction: 'incoming',
      event,
      args
    });
  });
  
  // Ghi lại các sự kiện đi
  const originalEmit = socket.emit;
  socket.emit = function(event, ...args) {
    if (!event.startsWith('internal:')) {
      eventLog.push({
        timestamp: Date.now(),
        socketId: socket.id,
        direction: 'outgoing',
        event,
        args: args.slice(0, -1)  // Loại bỏ callback nếu có
      });
    }
    return originalEmit.apply(this, [event, ...args]);
  };
});

// Điểm cuối để truy xuất các sự kiện đã ghi lại
app.get('/debug/socket-events', (req, res) => {
  res.json(eventLog);
});

// Điểm cuối để phát lại các sự kiện cho kiểm tra
app.post('/debug/replay-events', (req, res) => {
  const { events, targetSocketId } = req.body;
  const targetSocket = io.sockets.sockets.get(targetSocketId);
  
  if (!targetSocket) {
    return res.status(404).json({ error: 'Không tìm thấy socket mục tiêu' });
  }
  
  // Phát lại các sự kiện
  events.forEach(event => {
    if (event.direction === 'outgoing') {
      targetSocket.emit(event.event, ...event.args);
    }
  });
  
  res.json({ success: true, eventsReplayed: events.length });
});

Cách tiếp cận này đặc biệt giá trị cho việc tái tạo các chuỗi sự kiện phức tạp dẫn đến lỗi khó chẩn đoán, đặc biệt trong các kịch bản nhiều người dùng.

Các Thách Thức Gỡ Lỗi Socket.io Thường Gặp Và Giải Pháp

Mặc dù có các công cụ sẵn có, việc gỡ lỗi Socket.io gặp phải những thách thức độc đáo đòi hỏi các cách tiếp cận cụ thể. Dưới đây là một số vấn đề thường gặp và giải pháp của chúng:

Vấn Đề Thiết Lập Kết Nối

Khi các kết nối Socket.io không thể thiết lập, vấn đề thường nằm trong quá trình bắt tay. Một cách tiếp cận gỡ lỗi hệ thống bao gồm:

  1. Xác minh khả năng truyền tải: Kiểm tra xem WebSocket có khả dụng hay không hoặc xem các phương thức dự phòng có hoạt động hay không
  2. Xem xét điều kiện mạng: Tìm kiếm các tường lửa, proxy, hoặc các vấn đề CORS
  3. Kiểm tra các tham số bắt tay: Đảm bảo các mã xác thực và cookie được cấu hình đúng
// Gỡ lỗi kết nối nâng cao
const socket = io('https://example.com', {
  transports: ['websocket', 'polling'],  // Thử WebSocket trước, sau đó đến polling
  reconnectionAttempts: 3,               // Giới hạn các lần thử kết nối lại để phản hồi nhanh hơn
  timeout: 5000,                         // Thời gian chờ ngắn hơn để phát hiện lỗi nhanh hơn
  auth: { token: 'mã xác thực người dùng' },    // Dữ liệu xác thực
  query: { version: 'v1.2.3' },          // Tham số truy vấn
  debug: true                            // Kích hoạt gỡ lỗi tích hợp
});

// Xử lý sự kiện kết nối chi tiết
socket.on('connect', () => {
  console.log('Kết nối với ID:', socket.id);
  console.log('Phương thức sử dụng:', socket.io.engine.transport.name);
});

socket.on('connect_error', (error) => {
  console.error('Lỗi kết nối:', error);
  console.log('Số lần kết nối:', socket.io.engine.attempts);
});

socket.io.on('reconnect_attempt', (attempt) => {
  console.log(`Cố gắng kết nối lại ${attempt}`);
});

socket.io.on('reconnect_failed', () => {
  console.error('Không thể kết nối lại sau các lần thử tối đa');
});

Việc giám sát kết nối chi tiết này cung cấp cái nhìn giá trị về những gì đang diễn ra trong quá trình thiết lập kết nối, khiến việc xác định nguyên nhân gốc rễ của các vấn đề trở nên dễ dàng hơn.

Vấn Đề Xử Lý Sự Kiện Và Thời Gian

Xử lý sự kiện không đồng bộ trong Socket.io có thể dẫn đến điều kiện tranh chấp và lỗi liên quan đến thời gian. Việc gỡ lỗi hiệu quả yêu cầu:

  1. Ghi lại thứ tự sự kiện: Theo dõi thứ tự các sự kiện để xác định các mẫu không mong đợi
  2. Phân tích dấu thời gian: So sánh thời gian sự kiện để phát hiện chậm trễ hoặc thời gian chờ
  3. Theo dõi trạng thái: Giám sát thay đổi trạng thái ứng dụng theo phản hồi sự kiện
// Thời gian sự kiện và theo dõi trạng thái
let appState = { authenticated: false, rooms: [], lastEvent: null };

socket.onAny((event, ...args) => {
  const now = Date.now();
  const timeSinceLastEvent = appState.lastEvent ? now - appState.lastEvent.time : null;
  
  console.log(`[${new Date(now).toISOString()}] Sự kiện: ${event}`, {
    args,
    timeSinceLastEvent,
    currentState: { ...appState }
  });
  
  appState.lastEvent = { event, time: now, args };
});

// Cập nhật trạng thái dựa trên sự kiện
socket.on('authenticated', (userData) => {
  appState.authenticated = true;
  appState.user = userData;
});

socket.on('joined_room', (roomData) => {
  appState.rooms.push(roomData.roomId);
});

Cách tiếp cận này tạo ra một nhật ký toàn diện của các sự kiện và thay đổi trạng thái, khiến việc xác định nguồn gốc của các vấn đề liên quan đến thời gian dễ dàng hơn nhiều.

Rò Rỉ Bộ Nhớ Và Vấn Đề Hiệu Suất

Các ứng dụng Socket.io chạy lâu dài có thể bị rò rỉ bộ nhớ và suy giảm hiệu suất. Việc xác định những vấn đề này yêu cầu:

  1. Theo dõi lắng nghe: Giám sát số lượng lắng nghe sự kiện để phát hiện rò rỉ bộ nhớ tiềm năng
  2. Giám sát tài nguyên: Theo dõi mức sử dụng bộ nhớ và số lượng kết nối theo thời gian
  3. Chỉ số hiệu suất: Đo lường thời gian xử lý sự kiện và độ dài hàng đợi
// Giám sát bộ nhớ và hiệu suất
setInterval(() => {
  const memoryUsage = process.memoryUsage();
  const socketCount = Object.keys(io.sockets.sockets).length;
  const roomCount = io.sockets.adapter.rooms.size;
  
  console.log('Chỉ số máy chủ Socket.io:', {
    time: new Date().toISOString(),
    memory: {
      rss: Math.round(memoryUsage.rss / 1024 / 1024) + 'MB',
      heapTotal: Math.round(memoryUsage.heapTotal / 1024 / 1024) + 'MB',
      heapUsed: Math.round(memoryUsage.heapUsed / 1024 / 1024) + 'MB'
    },
    connections: {
      current: socketCount,
      peak: global.peakConnections || socketCount
    },
    rooms: roomCount,
    eventRate: (global.eventCount - (global.lastEventCount || 0)) / 30
  });
  
  global.lastEventCount = global.eventCount;
}, 30000);

// Theo dõi số lượng sự kiện
io.on('connection', (socket) => {
  socket.onAny(() => {
    global.eventCount = (global.eventCount || 0) + 1;
  });
});

Giám sát thường xuyên giúp xác định các xu hướng có thể chỉ ra rò rỉ bộ nhớ hoặc nút cổ chai hiệu suất trước khi chúng trở thành các vấn đề nghiêm trọng.

Hướng Dẫn Từng Bước Gỡ Lỗi Socket.io Với Apidog

Hãy cùng khám phá cách sử dụng công cụ gỡ lỗi Socket.io của Apidog một cách hiệu quả:

1. Tạo Một Điểm Cuối Socket.io Mới

Chú ý

a. Khởi động Apidog và điều hướng đến dự án của bạn

b. Tạo một điểm cuối Socket.io mới:

tạo điểm cuối Socket.IO mới tại Apidog

c. Cấu hình kết nối:

Cấu hình các thiết lập Socket.IO

2. Thiết Lập Và Giám Sát Kết Nối

Điều chỉnh các cài đặt nâng cao nếu cần thiết:

Điều chỉnh các cài đặt nâng cao cho điểm cuối Socket.IO

Thiết lập kết nối:

kết nối với Socket.IO

Quan sát quá trình bắt tay:

3. Làm Việc Với Các Sự Kiện Socket.io

Nghe các sự kiện:

Thêm sự kiện nghe

Gửi tin nhắn đến máy chủ:

Thêm nhiều tham số
Kích hoạt Ack để nhận trạng thái tin nhắn

Phân tích timeline giao tiếp:

phân tích kết quả gỡ lỗi Socket.IO

4. Sử Dụng Các Tính Năng Nâng Cao

Sử dụng biến cho thử nghiệm động:

Sử dụng biến trong các tham số

Lưu và tài liệu các điểm cuối Socket.io:

Tài liệu điểm cuối Socket.IO

Chia sẻ cấu hình với các thành viên trong nhóm:

chia sẻ tài liệu Socket.IO với các đồng nghiệp

So Sánh Cách Tiếp Cận Của Apidog Với Gỡ Lỗi Dựa Trên Mã

Khi so sánh công cụ gỡ lỗi Socket.io của Apidog với các cách tiếp cận dựa trên mã, một số điểm khác biệt chính trở nên rõ ràng:

Tầm Nhìn Và Ngữ Cảnh

Cách tiếp cận dựa trên mã:

// Ghi nhật ký phía máy chủ
io.on('connection', (socket) => {
  console.log('Khách hàng mới kết nối', socket.id);
  
  socket.onAny((event, ...args) => {
    console.log(`[${socket.id}] Nhận sự kiện: ${event}`, args);
  });
});

// Ghi nhật ký phía khách
socket.onAny((event, ...args) => {
  console.log(`Nhận sự kiện: ${event}`, args);
});

Cách tiếp cận này yêu cầu:

Cách tiếp cận của Apidog:

Năng Lực Tương Tác

Cách tiếp cận dựa trên mã:

// Khách thử nghiệm tùy chỉnh để kích hoạt sự kiện
const testEvent = (eventName, payload) => {
  console.log(`Gửi sự kiện thử nghiệm: ${eventName}`, payload);
  socket.emit(eventName, payload, (response) => {
    console.log(`Nhận xác nhận cho ${eventName}:`, response);
  });
};

// Gọi từ console
// testEvent('update-profile', { name: 'Alex' });

Cách tiếp cận này yêu cầu:

Cách tiếp cận của Apidog:

Hiệu Quả Khắc Phục Sự Cố

Cách tiếp cận dựa trên mã:

// Gỡ lỗi kết nối chi tiết
socket.io.on('reconnect_attempt', (attempt) => {
  console.log(`Cố gắng kết nối lại ${attempt}`);
  console.log('Tùy chọn phương thức:', socket.io.opts.transports);
  console.log('Thời gian chờ kết nối:', socket.io.opts.timeout);
});

socket.on('connect_error', (error) => {
  console.error('Lỗi kết nối:', error);
  console.log('Trạng thái kết nối:', socket.io.engine.readyState);
  console.log('Phương thức:', socket.io.engine.transport?.name);
});

Cách tiếp cận này yêu cầu:

Cách tiếp cận của Apidog:

Lợi Ích Của Việc Sử Dụng Apidog Để Gỡ Lỗi Socket.io

Công cụ gỡ lỗi Socket.io của Apidog mang lại một số lợi thế đáng kể so với các cách tiếp cận dựa trên mã:

  1. Giảm thời gian thiết lập: Không cần phải viết và duy trì mã gỡ lỗi tùy chỉnh
  2. Tầm nhìn toàn diện: Thấy cả hai phía của giao tiếp trong một giao diện duy nhất
  3. Thử nghiệm tương tác: Kích hoạt các sự kiện và quan sát phản hồi mà không cần thay đổi mã
  4. Thông tin về giao thức: Hiểu rõ về các giao thức Socket.io và Engine.io cơ bản
  5. Hợp tác nhóm: Chia sẻ cấu hình và phát hiện với các thành viên trong nhóm
  6. Tích hợp tài liệu: Tự động tài liệu các điểm cuối Socket.io bên cạnh các API khác

Đối với các nhóm phát triển, những lợi ích này chuyển thành kết quả hữu hình:

Kết Luận

Socket.io đã thay đổi cách các lập trình viên xây dựng các ứng dụng web thời gian thực, nhưng tính chất sự kiện hai chiều của nó đưa ra những thách thức gỡ lỗi độc đáo. Trong khi các cách tiếp cận gỡ lỗi dựa trên mã cung cấp cái nhìn quý giá, chúng thường yêu cầu nỗ lực thiết lập đáng kể và dẫn đến thông tin không đồng nhất trên các công cụ và nhật ký khác nhau.

Công cụ gỡ lỗi Socket.io của Apidog đại diện cho một bước tiến quan trọng trong cách các lập trình viên tiếp cận gỡ lỗi ứng dụng thời gian thực. Bằng cách cung cấp một giao diện thống nhất cho quản lý kết nối, giám sát sự kiện và thử nghiệm tương tác, nó giải quyết các thách thức cốt lõi đã khiến gỡ lỗi Socket.io trở nên khó khăn trong quá khứ.

Đối với các nhóm phát triển làm việc với Socket.io, việc áp dụng các công cụ gỡ lỗi chuyên biệt như Apidog có thể cải thiện một cách đáng kể năng suất và chất lượng mã. Khả năng quan sát, tương tác và khắc phục sự cố kết nối Socket.io trong thời gian thực—mà không phải viết mã gỡ lỗi tùy chỉnh—cho phép các lập trình viên tập trung vào việc xây dựng các tính năng thay vì đấu tranh với các công cụ.

Khi các tính năng thời gian thực ngày càng trở nên trung tâm đối với các ứng dụng web hiện đại, tầm quan trọng của các công cụ gỡ lỗi hiệu quả sẽ chỉ tăng lên. Bằng cách kết hợp các kỹ thuật gỡ lỗi dựa trên mã với các công cụ chế tạo sẵn như gỡ lỗi Socket.io của Apidog, các lập trình viên có thể đảm bảo rằng các ứng dụng thời gian thực của họ mang lại độ tin cậy và hiệu suất mà người dùng mong đợi.

Dù bạn đang xây dựng một ứng dụng trò chuyện, trình biên tập cộng tác, bảng điều khiển trực tiếp, hoặc bất kỳ tính năng thời gian thực nào khác, cách tiếp cận gỡ lỗi đúng đắn có thể tạo ra sự khác biệt giữa một trải nghiệm phát triển căng thẳng và một trải nghiệm hiệu quả. Với công cụ gỡ lỗi Socket.io của Apidog, trải nghiệm đó đã cải thiện một cách đáng kể.

Thực hành thiết kế API trong Apidog

Khám phá cách dễ dàng hơn để xây dựng và sử dụng API