Kenapa ini penting
Sejauh ini kita memakai fetch bawaan browser. fetch bagus, tapi untuk aplikasi yang memanggil puluhan endpoint, ada hal yang terus diulang: menulis URL lengkap, menambah header Content-Type, JSON.stringify body, cek res.ok, lalu res.json(). Lupa satu langkah, bug muncul.
axios adalah HTTP client yang paling banyak dipakai di ekosistem JavaScript. Ia mengurus konversi JSON otomatis, melempar error untuk status 4xx/5xx, mendukung timeout, dan punya fitur instance dan interceptor yang akan kita pakai di dua modul berikutnya. Versi yang dipakai: axios 1.20.0.
Konsep inti
Analogi: kurir langganan
fetch itu seperti kamu mengantar paket sendiri: harus bungkus sendiri, tulis alamat lengkap, cek sendiri apakah paket sampai. axios itu seperti kurir langganan yang sudah tahu alamat kantormu (baseURL), otomatis membungkus paket (JSON), dan langsung lapor kalau paket ditolak penerima (error 4xx/5xx).
Instalasi dan request pertama
npm install axios
import axios from "axios";
const res = await axios.get("https://jsonplaceholder.typicode.com/todos/1");
console.log(res.data); // → { userId: 1, id: 1, title: "delectus aut autem", completed: false }
Objek respons axios berisi:
| Properti | Isi |
|---|---|
data |
Body respons yang sudah di-parse dari JSON |
status |
Kode status HTTP, misalnya 200 atau 201 |
statusText |
Teks status |
headers |
Header respons |
config |
Konfigurasi yang dipakai untuk request ini |
Instance dan baseURL
Daripada menulis URL lengkap setiap kali, buat instance dengan konfigurasi default:
const api = axios.create({
baseURL: "https://jsonplaceholder.typicode.com",
timeout: 10000, // milidetik
headers: { Accept: "application/json" },
});
await api.get("/todos"); // → GET https://jsonplaceholder.typicode.com/todos
Instance juga membuatmu bisa punya beberapa konfigurasi berbeda, misalnya satu untuk API utama dan satu untuk layanan upload.
Empat method utama
| Method | Kegunaan | Penulisan axios |
|---|---|---|
| GET | Ambil data | api.get(url, config) |
| POST | Buat data baru | api.post(url, data, config) |
| PUT / PATCH | Ganti seluruh / sebagian data | api.put(url, data) / api.patch(url, data) |
| DELETE | Hapus data | api.delete(url, config) |
Perhatikan posisi argumen: untuk post, put, dan patch, argumen kedua adalah body, argumen ketiga adalah config. Untuk get dan delete, argumen kedua langsung config.
params: query string tanpa repot
api.get("/todos", { params: { userId: 1, _limit: 5 } });
// → GET /todos?userId=1&_limit=5
axios merangkai dan meng-encode query string untukmu, termasuk karakter khusus seperti spasi.
headers per request
api.post("/todos", { title: "Baru" }, { headers: { "X-Request-Source": "dashboard" } });
Header di sini digabung dengan header default instance.
Perbandingan dengan fetch
| Aspek | fetch | axios |
|---|---|---|
| Instalasi | Bawaan browser | npm install axios |
| Parse JSON | Manual res.json() |
Otomatis di res.data |
| Kirim JSON | Manual JSON.stringify + header |
Otomatis dari object |
| Error 404/500 | Tidak melempar, cek res.ok |
Otomatis melempar error |
| Timeout | Lewat AbortSignal.timeout(ms) |
Opsi timeout |
| Interceptor | Tidak ada | Ada (request & response) |
| Base URL | Tidak ada | axios.create({ baseURL }) |
| Ukuran | 0 KB | Belasan KB (gzip) |
fetch tetap pilihan bagus untuk proyek kecil atau saat ukuran bundle sangat penting. axios unggul ketika banyak endpoint membutuhkan konfigurasi yang sama.
Membaca status HTTP
Saat memakai API, kamu akan terus bertemu kode status. Kelompok besarnya:
| Kelompok | Arti | Contoh |
|---|---|---|
| 2xx | Berhasil | 200 OK, 201 Created, 204 No Content |
| 3xx | Dialihkan | 301, 304 Not Modified |
| 4xx | Kesalahan dari sisi pengirim (client) | 400, 401, 403, 404, 422 |
| 5xx | Kesalahan di server | 500, 502, 503 |
axios menganggap semua di luar 2xx sebagai error (bisa diubah lewat opsi validateStatus, tapi jarang perlu). Contoh nyata: saat kamu mengirim form daftar dengan email yang sudah terpakai, banyak API membalas 409 atau 422 beserta pesan. Dengan axios, pesan itu ada di error.response.data, dan modul berikutnya membahas cara menampilkannya dengan rapi.
Contoh kode
// src/lib/todoService.js, semua operasi CRUD tugas
import axios from "axios";
const api = axios.create({
baseURL: "https://jsonplaceholder.typicode.com",
timeout: 10000,
});
// GET dengan params
export async function getTodos({ userId, limit = 5 } = {}) {
const res = await api.get("/todos", { params: { userId, _limit: limit } });
return res.data;
}
// GET satu item
export async function getTodo(id) {
const { data } = await api.get(`/todos/${id}`);
return data;
}
// POST: argumen kedua = body (object biasa, axios mengubah ke JSON)
export async function createTodo(todo) {
const { data } = await api.post("/todos", todo);
return data;
}
// PUT: ganti seluruh data
export async function replaceTodo(id, todo) {
const { data } = await api.put(`/todos/${id}`, todo);
return data;
}
// PATCH: ubah sebagian
export async function updateTodo(id, perubahan) {
const { data } = await api.patch(`/todos/${id}`, perubahan);
return data;
}
// DELETE
export async function deleteTodo(id) {
await api.delete(`/todos/${id}`);
}
// src/Demo.jsx, mencoba service dengan tombol
import { useState } from "react";
import { createTodo, getTodos, updateTodo } from "./lib/todoService";
export default function Demo() {
const [log, setLog] = useState("");
async function jalankan(aksi) {
try {
const hasil = await aksi();
setLog(JSON.stringify(hasil, null, 2));
} catch (err) {
setLog(`Error: ${err.message}`);
}
}
return (
<div className="space-y-2 p-6">
<button onClick={() => jalankan(() => getTodos({ userId: 1, limit: 2 }))}>GET</button>
<button onClick={() => jalankan(() => createTodo({ title: "Belajar axios", completed: false, userId: 1 }))}>
POST
</button>
<button onClick={() => jalankan(() => updateTodo(1, { completed: true }))}>PATCH</button>
<pre className="rounded bg-slate-100 p-3 text-sm">{log}</pre>
</div>
);
}
// → GET: [{ "userId": 1, "id": 1, "title": "delectus aut autem", "completed": false }, {...}]
// → POST: { "title": "Belajar axios", "completed": false, "userId": 1, "id": 201 }
// → PATCH: { "userId": 1, "id": 1, "title": "delectus aut autem", "completed": true }
Sebagai perbandingan, ini createTodo versi fetch:
export async function createTodoFetch(todo) {
const res = await fetch("https://jsonplaceholder.typicode.com/todos", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(todo),
signal: AbortSignal.timeout(10000),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
}
Kesalahan umum
- Lupa bahwa data ada di
res.data.const todos = await api.get("/todos")berisi objek respons, bukan array. Pakaiconst { data } = await api.get(...). - Menaruh body di argumen yang salah.
api.post(url, { data: todo })mengirim body{ data: {...} }. Body cukup object langsung di argumen kedua. - Mengirim body di
api.get. GET tidak membawa body; pakaiparamsuntuk filter. - Merangkai query string manual.
"/todos?search=" + katabisa rusak kalaukatamengandung spasi atau&. Pakaiparams. - Menulis
baseURLdengan garis miring ganda.baseURL: ".../"ditambah"/todos"masih ditangani axios, tapi biasakan konsisten: baseURL tanpa garis miring akhir, path diawali/.
Latihan
- Mudah, Buat instance
apidengan baseURL jsonplaceholder, lalu ambil/usersdan tampilkan nama-namanya di console. - Sedang, Tulis fungsi
searchPosts(userId, limit)yang memakaiparamsuntuk mengambil post milik user tertentu dengan batas jumlah. - Menantang, Tulis ulang
todoServicedenganfetchmurni (tanpa axios) tapi tetap punyabaseURL, timeout, dan lempar error untuk status bukan 2xx. Bandingkan jumlah barisnya.
Lihat solusi
// 1.
import axios from "axios";
const api = axios.create({ baseURL: "https://jsonplaceholder.typicode.com" });
const { data } = await api.get("/users");
console.log(data.map((u) => u.name));
// → ["Leanne Graham", "Ervin Howell", ...] (10 nama)
// 2.
export async function searchPosts(userId, limit = 3) {
const { data } = await api.get("/posts", { params: { userId, _limit: limit } });
return data;
}
// searchPosts(2, 3) → 3 post milik userId 2
// 3. Versi fetch
const BASE = "https://jsonplaceholder.typicode.com";
async function request(path, { method = "GET", body, params } = {}) {
const url = new URL(BASE + path);
if (params) {
Object.entries(params).forEach(([k, v]) => v !== undefined && url.searchParams.set(k, v));
}
const res = await fetch(url, {
method,
headers: body ? { "Content-Type": "application/json" } : undefined,
body: body ? JSON.stringify(body) : undefined,
signal: AbortSignal.timeout(10000),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.status === 204 ? null : res.json();
}
export const getTodos = (userId) => request("/todos", { params: { userId, _limit: 5 } });
export const createTodo = (todo) => request("/todos", { method: "POST", body: todo });
// Lebih panjang karena kita menulis ulang fitur yang sudah ada di axios.
Ringkasan
- axios otomatis parse JSON (
res.data), mengirim object sebagai JSON, dan melempar error untuk 4xx/5xx. axios.create({ baseURL, timeout, headers })membuat instance dengan konfigurasi default.get/delete(url, config);post/put/patch(url, body, config).- Pakai
paramsuntuk query string danheadersuntuk header tambahan per request. fetchcocok untuk kebutuhan sederhana; axios unggul saat banyak endpoint berbagi konfigurasi.