HttpRequest adalah HTTP client JavaScript yang ringan dan powerful untuk memudahkan komunikasi dengan API. Dibangun di atas fetch API dengan berbagai fitur tambahan seperti interceptor, cache, retry logic, timeout, dan upload progress.
- Deskripsi
- Fitur
- Persyaratan
- Instalasi
- Cara Penggunaan
- API Reference
- Contoh Integrasi
- Troubleshooting
- Lisensi
Class ini dirancang untuk:
- Memudahkan manajemen API endpoint
- Menyediakan interface yang konsisten untuk berbagai jenis request
- Menangani kasus-kasus umum secara otomatis
- Menyediakan error handling yang lebih informatif
- Mendukung berbagai tipe data request dan response
- Meningkatkan produktivitas dengan fitur-fitur canggih
Dibangun di atas fetch API native dengan tambahan fitur yang biasanya hanya ada di library besar seperti Axios.
| Fitur | Status | Deskripsi |
|---|---|---|
| Base URL | β | Konfigurasi URL dasar untuk semua request |
| Default Headers | β | Header default yang bisa dikustomisasi |
| HTTP Methods | β | Mendukung GET, POST, PUT, DELETE |
| Query Parameters | β | Otomatis menangani parameter query untuk GET |
| Body Request | β | Otomatis memproses body untuk JSON dan form-urlencoded |
| Error Handling | β | Menangani error response dengan parsing otomatis |
| Response Parsing | β | Otomatis memparse response berdasarkan Content-Type |
| URL Validasi | β | Menangani konstruksi URL dengan benar |
| Timeout | β | Timeout otomatis untuk setiap request |
| Retry Logic | β | Retry otomatis saat gagal |
| Cache | β | Cache untuk GET request |
| Interceptors | β | Request & Response interceptors |
| Upload Progress | β | Tracking progress upload file |
| Abort Controller | β | Cancel request dengan AbortController |
| Universal | β | Support browser & Node.js |
- Browser: Chrome 60+, Firefox 60+, Safari 12+, Edge 79+
- Node.js: v16.0.0+ (dengan
fetchataunode-fetch) - JavaScript: ES6+ (Promise, async/await)
Salin HttpRequest.js ke direktori project Anda.
<script src="HttpRequest.js"></script>
<script>
const http = new HttpRequest('https://api.example.com');
</script>import HttpRequest from './HttpRequest.js';
const http = new HttpRequest('https://api.example.com');const HttpRequest = require('./HttpRequest.js');
const http = new HttpRequest('https://api.example.com');// Basic - tanpa base URL
const http = new HttpRequest();
// Dengan base URL
const api = new HttpRequest('https://api.example.com');
// Dengan default headers
const api = new HttpRequest('https://api.example.com', {
'Authorization': 'Bearer token123',
'X-API-Key': 'your-api-key',
'Accept': 'application/json'
});// GET dengan parameter query
api.get('/users', { page: 1, limit: 10 })
.then(data => console.log(data))
.catch(error => console.error(error));
// GET tanpa parameter
api.get('/users/1')
.then(data => console.log(data))
.catch(error => console.error(error));
// Async/Await
try {
const users = await api.get('/users', { page: 1 });
console.log(users);
} catch (error) {
console.error(error);
}// POST dengan JSON
api.post('/users', {
name: 'John Doe',
email: 'john@example.com',
age: 30
})
.then(data => console.log(data))
.catch(error => console.error(error));
// POST dengan async/await
try {
const result = await api.post('/users', {
name: 'Jane Doe',
email: 'jane@example.com'
});
console.log(result);
} catch (error) {
console.error(error);
}// Update data
api.put('/users/1', {
name: 'John Updated',
email: 'john.updated@example.com'
})
.then(data => console.log(data))
.catch(error => console.error(error));// Delete data
api.delete('/users/1')
.then(data => console.log(data))
.catch(error => console.error(error));
// DELETE dengan parameter (jarang, tapi bisa)
api.delete('/users', { data: { id: 1 } })
.then(data => console.log(data))
.catch(error => console.error(error));// Per-request headers
api.post('/upload', data, {
headers: {
'Custom-Header': 'custom-value',
'X-Request-ID': '12345'
}
});
// Header untuk semua request (di constructor)
const api = new HttpRequest('https://api.example.com', {
'Authorization': 'Bearer token123',
'X-Client-Version': '1.0.0'
});
// Override header untuk request tertentu
api.get('/users', null, {
headers: {
'Authorization': 'Bearer new-token-here' // Override
}
});// FormData untuk upload file
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('name', 'profile.jpg');
formData.append('description', 'User profile photo');
api.post('/upload', formData, {
headers: {
'Content-Type': undefined // Biarkan browser set boundary
}
})
.then(data => console.log(data))
.catch(error => console.error(error));
// Upload dengan progress tracking (method khusus)
const file = document.querySelector('input[type="file"]').files[0];
api.upload('/upload', file, (percent, event) => {
console.log(`Upload progress: ${percent}%`);
document.querySelector('.progress-bar').style.width = `${percent}%`;
})
.then(data => console.log('Upload success:', data))
.catch(error => console.error('Upload failed:', error));// Parameter query otomatis diubah menjadi URLSearchParams
api.get('/products', {
category: 'electronics',
minPrice: 100,
maxPrice: 1000,
sort: 'price_asc',
page: 1,
limit: 20
});
// Hasil URL:
// https://api.example.com/products?category=electronics&minPrice=100&maxPrice=1000&sort=price_asc&page=1&limit=20
// Parameter null/undefined akan diabaikan
api.get('/products', {
category: 'electronics',
brand: null, // Diabaikan
color: undefined // Diabaikan
});// Timeout 5 detik
api.get('/slow-endpoint', null, {
timeout: 5000 // 5 seconds
})
.then(data => console.log(data))
.catch(error => console.error(error));
// Retry 3 kali dengan delay 1 detik
api.post('/unstable-endpoint', data, {
retries: 3,
retryDelay: 1000 // 1 second
})
.then(data => console.log(data))
.catch(error => console.error(error));
// Kombinasi timeout dan retry
api.get('/unstable-endpoint', null, {
timeout: 3000,
retries: 3,
retryDelay: 500
});// Cache GET request (default TTL 60 detik)
api.get('/products', { category: 'electronics' }, {
cache: true,
cacheTTL: 30000 // 30 detik
})
.then(data => console.log(data));
// Clear cache
api.clearCache(); // Hapus semua cache
api.clearCache('GET:/products?category=electronics'); // Hapus spesifik// Request interceptor - modify request sebelum dikirim
api.addRequestInterceptor((context) => {
// Tambahkan timestamp ke setiap request
context.options.headers = {
...context.options.headers,
'X-Request-Time': Date.now()
};
return context;
});
// Response interceptor - modify response setelah diterima
api.addResponseInterceptor((data) => {
// Standardize response format
return {
success: true,
data: data,
timestamp: new Date().toISOString()
};
});
// Contoh logging interceptor
api.addRequestInterceptor((context) => {
console.log(`[${context.method}] ${context.url}`, context.data);
return context;
});
api.addResponseInterceptor((data) => {
console.log('Response:', data);
return data;
});// Upload file dengan progress bar
const fileInput = document.getElementById('fileInput');
const progressBar = document.getElementById('progressBar');
api.upload('/upload', fileInput.files[0], (percent, event) => {
progressBar.style.width = `${percent}%`;
progressBar.textContent = `${percent}%`;
if (percent === 100) {
console.log('Upload complete!');
}
})
.then(data => {
console.log('Upload success:', data);
alert('File uploaded successfully!');
})
.catch(error => {
console.error('Upload failed:', error);
alert('Upload failed!');
});// Buat AbortController
const controller = new AbortController();
// Kirim request dengan signal
api.get('/long-request', null, {
signal: controller.signal
})
.then(data => console.log(data))
.catch(error => {
if (error.name === 'AbortError') {
console.log('Request cancelled by user');
} else {
console.error(error);
}
});
// Batalkan request
controller.abort();// Try-catch dengan detail error
try {
const data = await api.get('/users/999');
console.log(data);
} catch (error) {
console.error('Status:', error.status);
console.error('Message:', error.message);
console.error('Data:', error.data);
console.error('URL:', error.url);
console.error('Method:', error.method);
if (error.status === 404) {
console.log('User not found');
} else if (error.status === 401) {
console.log('Unauthorized - redirect to login');
} else if (error.status === 500) {
console.log('Server error - try again later');
}
}
// Promise style
api.get('/users/999')
.then(data => console.log(data))
.catch(error => {
if (error.status === 404) {
console.log('Not found');
} else {
console.error('Error:', error.message);
}
});new HttpRequest(string baseURL = '', Object headers = {})| Parameter | Tipe | Default | Deskripsi |
|---|---|---|---|
baseURL |
string | '' |
Base URL untuk semua request |
headers |
Object | {} |
Default headers untuk semua request |
| Method | Parameter | Return | Deskripsi |
|---|---|---|---|
get() |
url, params = {}, options = {} |
Promise |
GET request |
post() |
url, data = null, options = {} |
Promise |
POST request |
put() |
url, data = null, options = {} |
Promise |
PUT request |
delete() |
url, options = {} |
Promise |
DELETE request |
upload() |
url, file, onProgress = null, options = {} |
Promise |
Upload file dengan progress |
request() |
method, url, data = null, options = {} |
Promise |
Generic request |
addRequestInterceptor() |
fn |
this |
Tambah request interceptor |
addResponseInterceptor() |
fn |
this |
Tambah response interceptor |
clearCache() |
key = null |
this |
Clear cache (spesifik atau semua) |
| Opsi | Tipe | Default | Deskripsi |
|---|---|---|---|
headers |
Object | {} |
Custom headers untuk request |
timeout |
number | 30000 |
Timeout dalam milidetik |
retries |
number | 0 |
Jumlah retry jika gagal |
retryDelay |
number | 1000 |
Delay antar retry (ms) |
cache |
boolean | false |
Enable cache untuk GET |
cacheTTL |
number | 60000 |
Cache TTL dalam milidetik |
signal |
AbortSignal | null |
Abort signal untuk cancel |
// api.js
import HttpRequest from './HttpRequest';
const api = new HttpRequest(process.env.REACT_APP_API_URL, {
'Authorization': `Bearer ${localStorage.getItem('token')}`,
'X-Client': 'React App'
});
// Request interceptor - refresh token
api.addRequestInterceptor((context) => {
const token = localStorage.getItem('token');
if (token) {
context.options.headers = {
...context.options.headers,
'Authorization': `Bearer ${token}`
};
}
return context;
});
// Response interceptor - handle 401
api.addResponseInterceptor((data) => {
// Standard response format
return data;
});
export default api;// Users.jsx
import React, { useState, useEffect } from 'react';
import api from './api';
function Users() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
fetchUsers();
}, []);
const fetchUsers = async () => {
setLoading(true);
setError(null);
try {
const data = await api.get('/users', { page: 1, limit: 10 });
setUsers(data);
} catch (error) {
setError(error.message);
console.error('Fetch users error:', error);
} finally {
setLoading(false);
}
};
const createUser = async (userData) => {
try {
const result = await api.post('/users', userData);
await fetchUsers(); // Refresh list
return result;
} catch (error) {
console.error('Create user error:', error);
throw error;
}
};
return (
<div>
{loading && <p>Loading...</p>}
{error && <p className="error">{error}</p>}
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}
export default Users;// api.js
import HttpRequest from './HttpRequest';
const api = new HttpRequest(import.meta.env.VITE_API_URL, {
'X-Client': 'Vue App'
});
// Interceptor untuk auth
api.addRequestInterceptor((context) => {
const token = localStorage.getItem('authToken');
if (token) {
context.options.headers = {
...context.options.headers,
'Authorization': `Bearer ${token}`
};
}
return context;
});
export default api;<!-- Users.vue -->
<template>
<div>
<div v-if="loading">Loading...</div>
<div v-if="error" class="error">{{ error }}</div>
<ul>
<li v-for="user in users" :key="user.id">
{{ user.name }}
</li>
</ul>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import api from './api';
const users = ref([]);
const loading = ref(false);
const error = ref(null);
onMounted(async () => {
loading.value = true;
error.value = null;
try {
users.value = await api.get('/users', { page: 1 });
} catch (err) {
error.value = err.message;
console.error(err);
} finally {
loading.value = false;
}
});
</script>// app.js
const HttpRequest = require('./HttpRequest');
const api = new HttpRequest('https://jsonplaceholder.typicode.com', {
'User-Agent': 'Node.js App'
});
// GET request
async function getPosts() {
try {
const posts = await api.get('/posts', { userId: 1 });
console.log('Posts:', posts.length);
return posts;
} catch (error) {
console.error('Error:', error.status, error.message);
throw error;
}
}
// POST request
async function createPost(data) {
try {
const result = await api.post('/posts', data);
console.log('Created post ID:', result.id);
return result;
} catch (error) {
console.error('Create error:', error.data);
throw error;
}
}
// Run
getPosts().then(posts => {
console.log('First post:', posts[0]?.title);
});
createPost({
title: 'My New Post',
body: 'This is the content...',
userId: 1
});Solusi: Install dan import node-fetch:
npm install node-fetch// Di awal file
const fetch = require('node-fetch');
// Atau untuk ES Module
import fetch from 'node-fetch';Solusi: Pastikan server mengirim header CORS yang sesuai:
// Atau gunakan mode cors di fetch
api.get('/endpoint', null, {
mode: 'cors',
credentials: 'include'
});Solusi: Tingkatkan timeout:
api.get('/slow-endpoint', null, {
timeout: 60000 // 60 detik
});Solusi: Pastikan Content-Type tidak diset untuk FormData:
api.upload('/upload', file, null, {
headers: {
'Content-Type': undefined // Penting!
}
});Solusi: Periksa apakah:
- Method adalah
GET cache: truediset- Belum expired (periksa
cacheTTL)
MIT License - Silahkan digunakan dan dimodifikasi sesuai kebutuhan.
- β Penambahan timeout & retry logic
- β Penambahan cache system
- β Penambahan interceptors
- β Penambahan upload progress
- β Penambahan AbortController support
- β Perbaikan URL handling
- β Perbaikan query params duplicate
- β Initial release
- β Basic HTTP methods
- β Headers management
- β Query params
- β Error handling
β Jika Anda menyukai library ini, berikan star di repository!