Fetch API trong JavaScript: Gọi API từ trình duyệt
Một số website tự động cập nhật dữ liệu — bài viết mới, thời tiết, thông báo — mà không cần bạn nhấn F5. Đằng sau đó, JavaScript đang gửi request đến server và nhận dữ liệu về. Công cụ thực hiện việc này là Fetch API.
Fetch API là cách chuẩn và hiện đại nhất để gọi API từ trình duyệt — không cần cài thêm thư viện, dựa trên Promise, và kết hợp tốt với async/await.
Sau bài này, bạn làm được: gửi GET và POST request, xử lý Response object đúng cách, viết error handling chuẩn, và hiểu khi nào nên dùng Fetch thay vì Axios.
Yêu cầu trước: Bạn đã biết async/await và hiểu cơ bản về API là gì.
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. Ra đời năm 2015, hiện hỗ trợ trên tất cả trình duyệt hiện đại.
Trước Fetch API, lập trình viên dùng XMLHttpRequest — cú pháp rườm rà, khó đọc:
// Cách cũ — 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.send();
Cùng chức năng đó, Fetch API viết ngắn hơn nhiều:
const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
const post = await response.json();
console.log(post);
Cú pháp cơ bản và 2 bước quan trọng
async function layBaiViet() {
const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
const post = await response.json();
console.log(post.title);
}
layBaiViet();
Fetch hoạt động theo 2 bước:
await fetch(url)— Chờ server phản hồi, trả về Response object (chứa header)await response.json()— Đọc body và chuyển JSON thành object JavaScript
Tại sao cần 2 bước? 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.
GET Request thực hành
Copy đoạn code này và chạy trong Console trình duyệt (F12 → Console) — dùng JSONPlaceholder, API miễn phí cho học tập:
async function layDanhSachBaiViet() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
if (!response.ok) {
throw new Error(`Lỗi server: ${response.status}`);
}
const posts = await response.json();
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();
Response Object — Các thuộc tính quan trọng
| Thuộc tính / Phương thức | Kiểu | Ý nghĩa |
|---|---|---|
response.ok | boolean | true nếu status 200–299 |
response.status | number | HTTP status code (200, 404, 500…) |
response.statusText | string | "OK", "Not Found"… |
response.json() | Promise | Parse JSON body thành object |
response.text() | Promise | Đọc body dưới dạng chuỗi |
POST Request — Gửi dữ liệu
async function dangBaiViet(tieuDe, noiDung) {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
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);
return baiMoi;
} catch (error) {
console.error('Lỗi POST:', error.message);
}
}
dangBaiViet('Tiêu đề thử nghiệm', 'Nội dung bài viết...');
3 điểm quan trọng khi dùng POST:
method: 'POST'— Mặc định là GET, phải chỉ định rõ'Content-Type': 'application/json'— Bắt buộc khi gửi JSONJSON.stringify()— Chuyển object thành chuỗi JSON trước khi gửi
Xử lý lỗi đúng cách — Điểm hay bị bỏ qua
Fetch API không tự động throw error khi nhận status 404 hay 500. Điều này khác với Axios — và là lý do nhiều người viết sai:
// SAI — Không bắt được lỗi HTTP
async function layDuLieuSai() {
try {
const response = await fetch('/api/khong-ton-tai');
const data = await response.json(); // Vẫn chạy dù server trả 404!
return data;
} catch (error) {
// Chỉ bắt lỗi MẠNG, không bắt lỗi HTTP
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');
if (!response.ok) {
throw new Error(`Lỗi HTTP: ${response.status}`);
}
return await response.json();
} catch (error) {
// Bắt được cả lỗi mạng VÀ lỗi HTTP
console.error('Lỗi:', error.message);
return null;
}
}
CORS là gì và cách xử lý
Khi học Fetch API, bạn sẽ gặp lỗi này:
Access to fetch at 'https://api.other-site.com/data' from origin
'http://localhost:3000' has been blocked by CORS policy.
CORS là cơ chế bảo mật của trình duyệt. Khi trang web của bạn (localhost:3000) gọi API từ domain khác, trình duyệt mặc định chặn lại.
Cách xử lý:
- Server thêm CORS header (cách đúng trong production):
Access-Control-Allow-Origin: * - Dùng proxy khi phát triển: Cấu hình proxy trong Vite hoặc webpack-dev-server
- Dùng API đã hỗ trợ CORS: JSONPlaceholder, OpenWeatherMap
Lưu ý: CORS là quy tắc của trình duyệt — cùng request đó gửi từ Postman hay Node.js thì hoàn toàn bình thường.
Fetch API so với Axios
| Tiêu chí | Fetch API | Axios |
|---|---|---|
| Cài đặt | Không cần (có sẵn) | npm install axios |
| Phát hiện lỗi HTTP | Phải tự kiểm tra response.ok | Tự động reject 4xx/5xx |
| Parse JSON | Phải gọi .json() thủ công | Tự động |
| Interceptor | Không có | Có (thêm token, log…) |
| Node.js support | Từ v18 trở lên | Mọi phiên bản |
| Kích thước | 0 KB | ~14 KB |
Khi nào dùng Fetch: Học tập, dự án cá nhân nhỏ, muốn tránh phụ thuộc thư viện ngoài.
Khi nào dùng Axios: Dự án team vừa-lớn, cần authentication tập trung, muốn error handling nhất quán.
Lời khuyên: Học Fetch trước để hiểu cơ chế — sau đó chuyển sang Axios sẽ rất nhanh.
Thực hành đầy đủ: CRUD với JSONPlaceholder
const BASE_URL = 'https://jsonplaceholder.typicode.com';
async function xuLyResponse(response) {
if (!response.ok) throw new Error(`Lỗi HTTP ${response.status}`);
return response.json();
}
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);
}
async function layBaiViet(id) {
const response = await fetch(`${BASE_URL}/posts/${id}`);
return xuLyResponse(response);
}
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: '...', 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 daXoa = await xoaBaiViet(1);
console.log('Đã xóa:', daXoa);
} catch (error) {
console.error('Có lỗi:', error.message);
}
}
main();
Câu hỏi thường gặp
Fetch có dùng được trên Internet Explorer không?
Không. IE đã ngừng hỗ trợ từ 6/2022 — dự án hiện đại không cần lo về IE.
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 chưa? (2) Đã dùng JSON.stringify() chưa? (3) Server có cấu hình CORS đúng không?
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/catchlà cách viết được khuyến nghị - Luôn kiểm tra
response.ok— Fetch không tự throw lỗi HTTP - POST cần
method,Content-Typeheader, 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
Bước tiếp theo: Tự viết một trang hiển thị danh sách từ JSONPlaceholder, 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 ứng dụng web đơn giản.