Cơ bảnKiến thức cơ bản

Fetch API trong JavaScript: Gọi API từ trình duyệt

8 phút đọc0 lượt xem
#fetch api#javascript#gọi api#async await#http request#axios vs fetch#cors

Fetch API trong JavaScript: Gọi API từ trình duyệt

Bạn có bao giờ thắc mắc tại sao một số website tự động cập nhật bài viết mới, dự báo thời tiết hay thông báo — mà bạn không cần nhấn F5? Đằng sau đó, JavaScript đang lặng lẽ gửi yêu cầu đến server và nhận dữ liệu về. Công cụ thực hiện việc đó chính là Fetch API.

Fetch API là cách chuẩn, hiện đại nhất để gọi API từ trình duyệt bằng JavaScript — không cần cài thêm thư viện, dựa trên Promise, và kết hợp hoàn hảo với async/await.

Sau bài này, bạn sẽ làm được:

  • Gửi GET và POST request từ JavaScript
  • Xử lý Response object đúng cách
  • Viết error handling chuẩn với Fetch API
  • Hiểu sự khác biệt giữa Fetch và Axios để chọn đúng công cụ

Yêu cầu: Bạn đã biết async/await (xem lại tại đây). Nếu chưa hiểu API là gì, hãy đọc bài đó trước nhé.

Fetch API là gì?

Fetch API là tính năng tích hợp sẵn trong trình duyệt cho phép JavaScript gửi HTTP request đến server và nhận dữ liệu về. Ra đời năm 2015, hiện đã được hỗ trợ trên tất cả trình duyệt hiện đại.

Trước khi có Fetch API, lập trình viên phải dùng XMLHttpRequest (viết tắt là XHR) — cú pháp rườm rà và khó đọc:

// Cách cũ dùng XMLHttpRequest — không cần học nữa
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1');
xhr.onload = function() {
  if (xhr.status === 200) {
    console.log(JSON.parse(xhr.responseText));
  }
};
xhr.onerror = function() {
  console.error('Request thất bại');
};
xhr.send();

Cùng chức năng đó, Fetch API viết gọn hơn nhiều:

// Cách mới dùng Fetch API — hiện đại, dễ đọc
const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
const post = await response.json();
console.log(post);

Ưu điểm của Fetch API so với XHR:

  • Không cần cài đặt — có sẵn trong mọi trình duyệt hiện đại
  • Dựa trên Promise — kết hợp tự nhiên với async/await
  • Cú pháp trực quan — dễ đọc, dễ viết hơn nhiều
  • Hỗ trợ đầy đủ — Chrome, Firefox, Safari, Edge đều dùng được

Cú pháp cơ bản và cách dùng với async/await

Hàm fetch() nhận vào một URL và trả về Promise. Khi dùng với async/await, code trở nên rất dễ đọc:

// Cú pháp cơ bản
async function layBaiViet() {
  const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
  const post = await response.json();
  console.log(post.title);
}

layBaiViet();

Lưu ý: fetch() hoạt động theo 2 bước:

  1. await fetch(url) — Chờ server phản hồi (trả về Response object chứa header)
  2. await response.json() — Đọc phần body và chuyển đổi JSON thành object JavaScript

Tại sao 2 bước? Vì dữ liệu được truyền theo luồng (stream). Header đến trước, body đến sau. Thiết kế này cho phép xử lý file lớn mà không cần chờ toàn bộ dữ liệu tải xong.

Nếu bạn chưa quen với async/await, hãy xem lại bài Async/Await trong JavaScript trước khi tiếp tục.

Thực hành: GET Request với JSONPlaceholder

JSONPlaceholder là API miễn phí dành cho học tập và kiểm thử. URL gốc: https://jsonplaceholder.typicode.com. Bạn có thể gọi trực tiếp mà không cần đăng ký.

Hãy copy đoạn code này và chạy trong Console của trình duyệt (F12 → Console):

async function layDanhSachBaiViet() {
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/posts');
    
    // Luôn kiểm tra status trước khi đọc dữ liệu
    if (!response.ok) {
      throw new Error(`Lỗi server: ${response.status}`);
    }
    
    const posts = await response.json();
    
    // Hiển thị 3 bài đầu tiên
    posts.slice(0, 3).forEach(post => {
      console.log(`[${post.id}] ${post.title}`);
    });
    
  } catch (error) {
    console.error('Không lấy được dữ liệu:', error.message);
  }
}

layDanhSachBaiViet();

Kết quả sẽ hiển thị 3 tiêu đề bài viết từ server.

Response Object — Các thuộc tính quan trọng

Thuộc tính / Phương thức Kiểu dữ liệu Ý nghĩa
response.ok boolean true nếu status từ 200 đến 299
response.status number HTTP status code (200, 404, 500...)
response.statusText string "OK", "Not Found", "Internal Server Error"...
response.json() Promise Parse JSON body thành JavaScript object
response.text() Promise Đọc body dưới dạng chuỗi văn bản

Gửi dữ liệu với POST Request

GET là "lấy dữ liệu", còn POST là "gửi dữ liệu". Ví dụ: đăng bài viết mới, gửi form đăng ký tài khoản.

async function dangBaiViet(tieuDe, noiDung) {
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
      method: 'POST',                           // Chỉ định HTTP method
      headers: {
        'Content-Type': 'application/json',     // Báo với server: tôi gửi JSON
      },
      body: JSON.stringify({                    // Chuyển object thành chuỗi JSON
        title: tieuDe,
        body: noiDung,
        userId: 1,
      }),
    });
    
    if (!response.ok) {
      throw new Error(`Đăng bài thất bại: ${response.status}`);
    }
    
    const baiMoi = await response.json();
    console.log('Đã tạo bài viết với ID:', baiMoi.id); // 101
    return baiMoi;
    
  } catch (error) {
    console.error('Lỗi POST:', error.message);
  }
}

dangBaiViet('Tiêu đề thử nghiệm', 'Nội dung bài viết...');

Ba điểm quan trọng khi dùng POST:

  1. method: 'POST' — Mặc định là GET, phải chỉ định rõ khi dùng POST/PUT/DELETE
  2. 'Content-Type': 'application/json' — Bắt buộc khi gửi JSON, không có header này server có thể không hiểu dữ liệu
  3. JSON.stringify() — Chuyển object JavaScript sang chuỗi JSON trước khi gửi

Để kiểm tra API request/response trực quan hơn, bạn có thể dùng Postman — công cụ test API rất phổ biến trong cộng đồng lập trình viên.

Xử lý lỗi đúng cách với Fetch API

Đây là điểm nhiều người mắc sai lầm. Fetch API không tự động throw error khi nhận được status 404 hay 500. Điều này khác với Axios!

// SAI — Cách viết không đúng
async function layDuLieuSai() {
  try {
    const response = await fetch('/api/khong-ton-tai');
    const data = await response.json(); // Vẫn chạy đến đây dù server trả về 404!
    return data;
  } catch (error) {
    // Chỉ bắt được lỗi MẠNG (mất kết nối internet, DNS lỗi...)
    // KHÔNG bắt được lỗi HTTP (404, 500...)
    console.error('Lỗi:', error);
  }
}
// ĐÚNG — Kết hợp response.ok với try/catch
async function layDuLieuDung() {
  try {
    const response = await fetch('/api/du-lieu');
    
    // Bước 1: Kiểm tra HTTP status
    if (!response.ok) {
      throw new Error(`Lỗi HTTP: ${response.status}`);
    }
    
    // Bước 2: Đọc dữ liệu
    const data = await response.json();
    return data;
    
  } catch (error) {
    // Bắt được CẢ lỗi mạng VÀ lỗi HTTP (throw thủ công ở trên)
    console.error('Lỗi:', error.message);
    return null;
  }
}

Các loại lỗi bạn có thể gặp:

Loại lỗi Nguyên nhân Cách phát hiện
Lỗi mạng Mất internet, DNS lỗi, server không phản hồi Tự động vào catch
Lỗi HTTP 4xx 404 Not Found, 401 Unauthorized, 403 Forbidden Kiểm tra response.ok
Lỗi HTTP 5xx 500 Internal Server Error, 503 Service Unavailable Kiểm tra response.ok
Lỗi parse JSON Server trả về HTML thay vì JSON Tự động vào catch

CORS là gì và tại sao bạn gặp lỗi này?

Khi học Fetch API, sớm muộn bạn sẽ gặp lỗi này trong Console:

Access to fetch at 'https://api.other-site.com/data' from origin 
'http://localhost:3000' has been blocked by CORS policy: 
No 'Access-Control-Allow-Origin' header is present

CORS (Cross-Origin Resource Sharing) là cơ chế bảo mật của trình duyệt. Khi trang web của bạn (localhost:3000) cố gắng gọi API từ domain khác (api.other-site.com), trình duyệt mặc định chặn lại để bảo vệ người dùng.

Cách giải quyết:

  1. Server thêm CORS header (cách đúng trong môi trường thực tế): Server trả về header Access-Control-Allow-Origin: * hoặc chỉ định domain cụ thể
  2. Dùng proxy khi phát triển: Cấu hình proxy trong Vite hoặc webpack-dev-server để chuyển tiếp request
  3. Dùng API đã hỗ trợ CORS: JSONPlaceholder, OpenWeatherMap... đã cho phép truy cập từ mọi domain

Lưu ý quan trọng: CORS là quy tắc của trình duyệt, không phải của JavaScript hay server. Cùng request đó, nếu gửi từ Postman hay Node.js thì hoàn toàn bình thường. Vậy nên khi bị CORS, đừng nghĩ code của bạn sai — hãy cấu hình CORS ở phía server hoặc dùng proxy.

So sánh Fetch API và Axios

Khi bắt đầu làm dự án thực tế, câu hỏi thường gặp là: dùng Fetch API hay Axios?

Tiêu chí Fetch API Axios
Cài đặt Không cần (có sẵn trong trình duyệt) npm install axios
Phát hiện lỗi HTTP Phải tự kiểm tra response.ok Tự động reject khi 4xx/5xx
Parse JSON tự động Không (phải gọi .json() thủ công)
Cài timeout Phải viết thêm code Tùy chọn timeout đơn giản
Interceptor Không có Có (tự động thêm token xác thực...)
Hỗ trợ Node.js Từ Node.js v18 trở lên Mọi phiên bản
Kích thước bundle 0 KB ~14 KB

Khi nào dùng Fetch API: Học tập, dự án cá nhân nhỏ, muốn tránh phụ thuộc thư viện bên ngoài.

Khi nào dùng Axios: Dự án team quy mô vừa đến lớn, cần xử lý authentication tập trung, muốn error handling thống nhất.

Lời khuyên: Học Fetch API trước để hiểu cơ bản, sau đó chuyển sang Axios khi cần tính năng nâng cao.

Thực hành đầy đủ: CRUD với JSONPlaceholder

Đây là ví dụ đầy đủ với cả 4 HTTP method — GET, POST, PUT, DELETE:

const BASE_URL = 'https://jsonplaceholder.typicode.com';

// Hàm tiện ích để xử lý response
async function xuLyResponse(response) {
  if (!response.ok) {
    throw new Error(`Lỗi HTTP ${response.status}: ${response.statusText}`);
  }
  return response.json();
}

// CREATE — Tạo bài viết mới (POST)
async function taoBaiViet(duLieu) {
  const response = await fetch(`${BASE_URL}/posts`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(duLieu),
  });
  return xuLyResponse(response);
}

// READ — Lấy một bài viết (GET)
async function layBaiViet(id) {
  const response = await fetch(`${BASE_URL}/posts/${id}`);
  return xuLyResponse(response);
}

// UPDATE — Cập nhật bài viết (PUT)
async function capNhatBaiViet(id, duLieu) {
  const response = await fetch(`${BASE_URL}/posts/${id}`, {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(duLieu),
  });
  return xuLyResponse(response);
}

// DELETE — Xóa bài viết
async function xoaBaiViet(id) {
  const response = await fetch(`${BASE_URL}/posts/${id}`, {
    method: 'DELETE',
  });
  return response.ok;
}

// Chạy thử
async function main() {
  try {
    const baiMoi = await taoBaiViet({
      title: 'Học Fetch API',
      body: 'Fetch API rất dễ dùng!',
      userId: 1,
    });
    console.log('Đã tạo bài viết, ID:', baiMoi.id);

    const bai = await layBaiViet(1);
    console.log('Tiêu đề:', bai.title);

    const baiCapNhat = await capNhatBaiViet(1, {
      title: 'Tiêu đề mới',
      body: 'Nội dung đã cập nhật',
      userId: 1,
    });
    console.log('Đã cập nhật:', baiCapNhat.title);

    const daXoa = await xoaBaiViet(1);
    console.log('Đã xóa thành công:', daXoa);

  } catch (error) {
    console.error('Có lỗi xảy ra:', error.message);
  }
}

main();

Câu hỏi thường gặp

Fetch API có dùng được trên Internet Explorer không?

Không. IE đã ngừng hỗ trợ từ tháng 6/2022. Các dự án hiện đại không cần lo về IE.

Có thể dùng .then() thay vì async/await không?

Hoàn toàn được. Async/await chỉ là cú pháp đẹp hơn, bên trong vẫn là Promise. Nhưng với code mới, async/await được khuyến khích vì dễ đọc hơn.

Tại sao POST của mình gửi nhưng server không nhận được dữ liệu?

Kiểm tra 3 điều: (1) Đã thêm Content-Type: application/json vào headers chưa? (2) Đã dùng JSON.stringify() cho body chưa? (3) Server có cấu hình CORS đúng chưa?

Nên học Fetch trước hay Axios trước?

Học Fetch trước. Fetch là chuẩn của trình duyệt, hiểu rõ Fetch giúp bạn nắm vững cơ chế HTTP request. Sau đó chuyển sang Axios sẽ rất nhanh vì API tương tự nhau.

Tóm tắt

  • Fetch API là cách chuẩn để gọi HTTP request từ trình duyệt — không cần cài đặt
  • Dùng async/await + try/catch là cách viết hiện đại và được khuyến khích
  • Luôn kiểm tra response.ok vì Fetch không tự throw lỗi khi nhận 4xx/5xx
  • POST request cần có method, Content-Type header, và JSON.stringify() cho body
  • CORS là giới hạn của trình duyệt — cần server cho phép hoặc dùng proxy
  • Fetch phù hợp cho học tập; Axios phù hợp hơn cho dự án lớn cần quản lý phức tạp

Bước tiếp theo: Hãy thử tự viết một trang hiển thị danh sách từ JSONPlaceholder, rồi thêm nút "Xóa" dùng DELETE request. Kết hợp với kiến thức JavaScript cơ bản và bạn đã có thể tự xây dựng ứng dụng web đơn giản rồi đó!

Về tác giả

Ảnh đại diện tác giả Kenji — họa tiết hình học

Kenji

Kỹ sư phần mềm full-stack (Web), hơn 5 năm kinh nghiệm thực tế

  • Python
  • DB
  • Hạ tầng
  • Đào tạo & cố vấn
  • AI

Làm việc cùng đồng nghiệp người Việt, tôi thấy thiếu tài liệu kỹ thuật rõ ràng bằng tiếng Việt. codeahoc là nơi tôi chia sẻ theo hướng thực tế, dễ áp dụng.

Nguyên tắc nội dung

  • Ưu tiên nguồn gốc và góc nhìn từ thực tế triển khai.
  • Nếu có sai sót, nội dung sẽ được cập nhật và sửa kịp thời.

Khóa học liên quan

The Complete JavaScript Course 2026: From Zero to Expert!

Khóa học JavaScript toàn diện nhất từ cơ bản đến nâng cao.

4.7499.000 ₫
Xem khóa học →

React - The Complete Guide (incl. React Router & Redux)

Làm chủ React.js với các dự án thực tế, hooks, Redux.

4.6499.000 ₫
Xem khóa học →

Node.js, Express, MongoDB & More: The Complete Bootcamp

Backend với Node.js: REST API, authentication, MongoDB.

4.7499.000 ₫
Xem khóa học →
Quảng cáo