Dùng fetch để gọi API trong JavaScript: GET, POST và xử lý lỗi

  • author-image

    Nguyễn Đình Cường

  • blog-tag
  • blog-comment 0 comment
  • created-date 09 Nov, 2026
blog-thumbnail

Hầu hết ứng dụng web ngày nay đều lấy dữ liệu từ API: danh sách sản phẩm, thông tin người dùng, kết quả tìm kiếm. Trình duyệt có sẵn hàm fetch để gửi yêu cầu HTTP mà không cần cài thêm thư viện nào. Bài này hướng dẫn cách dùng đúng, kể cả phần xử lý lỗi mà nhiều người bỏ sót.

Gửi yêu cầu GET đơn giản

async function layBaiViet() {
  const response = await fetch('https://api.example.com/posts');
  const duLieu = await response.json();
  console.log(duLieu);
}

fetch trả về promise của đối tượng Response. Để lấy nội dung, bạn gọi tiếp một phương thức cũng trả về promise: response.json() cho JSON, response.text() cho chữ thuần, response.blob() cho file nhị phân. Địa chỉ trong ví dụ chỉ mang tính minh họa.

Cạm bẫy lớn nhất: fetch không báo lỗi HTTP

Nhiều người ngạc nhiên khi fetch không ném lỗi với mã trạng thái 404 hay 500. Promise chỉ bị reject khi có lỗi mạng (mất kết nối, bị chặn). Với phản hồi 404, nó vẫn "thành công" nhưng response.ok là false. Vì vậy hãy luôn kiểm tra:

async function layJson(url) {
  const response = await fetch(url);
  if (!response.ok) {
    throw new Error('Lỗi HTTP ' + response.status);
  }
  return response.json();
}

Hàm tiện ích nhỏ này dùng lại được ở khắp nơi và gom việc kiểm tra về một chỗ.

Gửi dữ liệu bằng POST

async function taoBaiViet(tieuDe, noiDung) {
  const response = await fetch('https://api.example.com/posts', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ tieuDe, noiDung }),
  });
  if (!response.ok) throw new Error('Không tạo được bài viết');
  return response.json();
}

Ba điểm cần nhớ: đặt method, khai báo header Content-Type là JSON, và chuyển object thành chuỗi bằng JSON.stringify cho body. Với PUT, PATCH, DELETE bạn làm tương tự, chỉ đổi method. Khi gửi form có file, hãy dùng đối tượng FormData làm body và đừng tự đặt Content-Type, để trình duyệt thêm giá trị boundary đúng.

Xử lý lỗi trọn vẹn và trạng thái tải

Trong giao diện thật, bạn cần ba trạng thái: đang tải, thành công, lỗi. Mẫu dưới đây thể hiện cách nghĩ đó:

async function hienThiDanhSach() {
  const vung = document.querySelector('#ket-qua');
  vung.textContent = 'Đang tải...';
  try {
    const bai = await layJson('https://api.example.com/posts');
    vung.textContent = 'Có ' + bai.length + ' bài viết';
  } catch (loi) {
    vung.textContent = 'Không tải được dữ liệu. Vui lòng thử lại.';
    console.error(loi);
  }
}

Thông báo lỗi nên thân thiện với người dùng, còn chi tiết kỹ thuật chỉ ghi vào console hoặc hệ thống theo dõi lỗi.

Hủy yêu cầu và đặt thời gian chờ

Khi người dùng gõ tìm kiếm liên tục, bạn muốn hủy yêu cầu cũ. AbortController làm được việc đó:

let controller;
async function timKiem(tuKhoa) {
  if (controller) controller.abort();
  controller = new AbortController();
  try {
    const res = await fetch('/api/search?q=' + encodeURIComponent(tuKhoa), {
      signal: controller.signal,
    });
    return await res.json();
  } catch (loi) {
    if (loi.name === 'AbortError') return null; // bị hủy, bỏ qua
    throw loi;
  }
}

Hàm encodeURIComponent bảo đảm từ khóa chứa ký tự đặc biệt hay tiếng Việt có dấu vẫn được gửi đúng.

CORS, xác thực và bảo mật

  • Nếu gọi API ở tên miền khác, máy chủ phải cho phép bằng CORS, nếu không trình duyệt sẽ chặn phản hồi. Đây là cơ chế của trình duyệt, không phải lỗi của fetch.
  • Gửi token bằng header Authorization. Đừng nhúng khóa bí mật thật sự vào mã trình duyệt vì ai cũng đọc được.
  • Muốn gửi kèm cookie sang tên miền khác, đặt credentials: 'include' và cấu hình máy chủ tương ứng.

Gói fetch thành một client dùng lại

Khi dự án lớn dần, bạn sẽ không muốn lặp lại header và kiểm tra lỗi ở mọi nơi. Hãy tạo một hàm bọc:

const BASE_URL = 'https://api.example.com';

async function api(duongDan, tuyChon = {}) {
  const res = await fetch(BASE_URL + duongDan, {
    headers: { 'Content-Type': 'application/json' },
    ...tuyChon,
    body: tuyChon.body ? JSON.stringify(tuyChon.body) : undefined,
  });
  if (!res.ok) throw new Error('HTTP ' + res.status);
  return res.status === 204 ? null : res.json();
}

// sử dụng
const bai = await api('/posts');
await api('/posts', { method: 'POST', body: { tieuDe: 'Xin chào' } });

Tại một chỗ duy nhất bạn có thể thêm token xác thực, ghi log hay xử lý trường hợp token hết hạn. Đây cũng là cách thư viện như Axios ra đời, nhưng với nhu cầu đơn giản, vài dòng tự viết là đủ.

Kết luận

Với fetch, hãy nhớ: kiểm tra response.ok, đặt đúng header và body khi POST, bọc trong try/catch, và hủy yêu cầu thừa bằng AbortController. Gói những điều này vào vài hàm tiện ích sẽ giúp toàn bộ ứng dụng của bạn gọn và nhất quán.

Nếu bạn muốn xây một ứng dụng hoàn chỉnh có gọi API từ đầu đến cuối, hãy xem các khóa học tại codebem.com.

author_photo
Nguyễn Đình Cường

0 comment