
Security News
Axios Supply Chain Attack Reaches OpenAI macOS Signing Pipeline, Forces Certificate Rotation
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.
@zogjs/http
Advanced tools
Powerful HTTP client plugin for Zog.js - Full-featured request library with interceptors, file upload/download progress tracking, and reactive state management
A powerful, production-ready HTTP client plugin for Zog.js with full support for all HTTP methods, file uploads with progress tracking, request/response interceptors, and reactive state management.
<script type="module">
import { createApp } from 'zogjs';
import { ZogHttpPlugin, $http } from '@zogjs/http';
createApp(() => ({
// your data
}))
.use(ZogHttpPlugin, {
baseURL: 'https://api.example.com',
timeout: 30000,
})
.mount('#app');
</script>
<div id="app">
<div z-if="$http.state.loading">Loading...</div>
<div z-if="$http.state.error">{{ $http.state.error }}</div>
<ul>
<li z-for="user in users" :key="user.id">{{ user.name }}</li>
</ul>
<button @click="loadUsers">Load Users</button>
</div>
<script type="module">
import { createApp, reactive } from './zog.js';
import { ZogHttpPlugin, $http } from './zog-http.js';
createApp(() => {
const users = reactive([]);
async function loadUsers() {
try {
const response = await $http.get('/users');
users.splice(0, users.length, ...response.data);
} catch (error) {
console.error('Failed to load users:', error);
}
}
return { users, loadUsers };
})
.use(ZogHttpPlugin, { baseURL: 'https://api.example.com' })
.mount('#app');
</script>
.use(ZogHttpPlugin, {
// Base URL for all requests
baseURL: 'https://api.example.com',
// Default timeout in milliseconds
timeout: 30000,
// Default headers for all requests
headers: {
'Content-Type': 'application/json',
'X-Custom-Header': 'value',
},
// Include credentials (cookies) in requests
withCredentials: false,
// Number of automatic retries on failure
retries: 0,
// Delay between retries in milliseconds
retryDelay: 1000,
})
All methods return a Promise that resolves to a response object:
// Response object structure
{
data: any, // Parsed response body
status: number, // HTTP status code
statusText: string, // HTTP status text
headers: object, // Response headers
config: object, // Request configuration
request: object, // Original request info
}
// Simple GET
const response = await this.$http.get('/users');
// GET with query parameters
const response = await this.$http.get('/users', {
params: { page: 1, limit: 10, status: 'active' }
});
// GET with custom headers
const response = await this.$http.get('/users', {
headers: { 'X-Request-ID': '123' }
});
// Simple POST
const response = await this.$http.post('/users', {
name: 'John Doe',
email: 'john@example.com'
});
// POST with additional options
const response = await this.$http.post('/users',
{ name: 'John' },
{ headers: { 'X-Custom': 'value' } }
);
const response = await this.$http.put('/users/123', {
name: 'Jane Doe',
email: 'jane@example.com'
});
const response = await this.$http.patch('/users/123', {
name: 'Updated Name'
});
const response = await this.$http.delete('/users/123');
// DELETE with body
const response = await this.$http.delete('/users', {
body: { ids: [1, 2, 3] }
});
const headResponse = await this.$http.head('/users');
const optionsResponse = await this.$http.options('/users');
For convenience, shorthand methods are also injected into scope:
await this.$get('/users');
await this.$post('/users', data);
await this.$put('/users/1', data);
await this.$patch('/users/1', data);
await this.$delete('/users/1');
// Set token
this.$http.setAuthToken('your-jwt-token');
// Clear token
this.$http.clearAuth();
// Or set directly
this.$http.setHeader('Authorization', 'Bearer your-token');
this.$http.setBasicAuth('username', 'password');
// Set single header
this.$http.setHeader('X-API-Key', 'your-api-key');
// Set multiple headers
this.$http.setHeaders({
'X-API-Key': 'key',
'X-Client-Version': '1.0.0'
});
// Remove header
this.$http.removeHeader('X-API-Key');
// Change base URL at runtime
this.$http.setBaseURL('https://api.newdomain.com');
// Set global timeout
this.$http.setTimeout(60000); // 60 seconds
// Per-request timeout
await this.$http.get('/slow-endpoint', { timeout: 120000 });
Interceptors allow you to run code before requests are sent and after responses are received.
// Add request interceptor
const interceptorId = this.$http.addRequestInterceptor(
// Success handler (called before each request)
async (config) => {
// Add timestamp to all requests
config.headers['X-Request-Time'] = Date.now();
// Add token from storage
const token = localStorage.getItem('token');
if (token) {
config.headers['Authorization'] = `Bearer ${token}`;
}
console.log('Request:', config.method, config.url);
return config; // Must return config
},
// Error handler (optional)
async (error) => {
console.error('Request error:', error);
throw error;
}
);
// Remove interceptor later
this.$http.removeRequestInterceptor(interceptorId);
// Add response interceptor
const interceptorId = this.$http.addResponseInterceptor(
// Success handler
async (response) => {
console.log('Response:', response.status, response.data);
// Transform response data
if (response.data?.items) {
response.data = response.data.items;
}
return response;
},
// Error handler
async (error) => {
if (error.status === 401) {
// Handle unauthorized - redirect to login
window.location.href = '/login';
}
if (error.status === 429) {
// Handle rate limiting - wait and retry
await new Promise(r => setTimeout(r, 5000));
return this.$http.request(error.request);
}
throw error;
}
);
this.$http.clearInterceptors();
The upload method provides real-time progress tracking and supports single/multiple files.
// Single file upload
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];
const { promise, tracker, abort } = this.$http.upload('/upload', file);
// Access reactive progress state
console.log(tracker.progress); // 0-100
console.log(tracker.status); // 'idle' | 'uploading' | 'completed' | 'error'
const response = await promise;
const { promise, tracker, abort } = this.$http.upload('/upload', file, {
// Field name for the file
fieldName: 'document',
// Additional form data
additionalData: {
description: 'My file',
category: 'documents'
},
// Custom headers
headers: {
'X-Upload-ID': 'unique-id'
},
// Progress callback
onProgress: (info) => {
console.log(`Progress: ${info.progress}%`);
console.log(`Speed: ${(info.speed / 1024).toFixed(2)} KB/s`);
console.log(`Remaining: ${info.remainingTime}s`);
},
// Completion callback
onComplete: (response) => {
console.log('Upload complete!', response.data);
},
// Error callback
onError: (error) => {
console.error('Upload failed:', error.message);
}
});
const files = document.querySelector('input[type="file"]').files;
const { promise, tracker } = this.$http.upload('/upload', Array.from(files), {
fieldName: 'files',
additionalData: { albumId: '123' }
});
const formData = new FormData();
formData.append('file', file);
formData.append('name', 'custom name');
const { promise } = this.$http.upload('/upload', formData);
const { promise, abort, requestId } = this.$http.upload('/upload', file);
// Cancel by abort function
abort();
// Or cancel by request ID
this.$http.cancelRequest(requestId);
<div id="app">
<input type="file" @change="handleFileSelect" />
<div z-if="uploadState.status === 'uploading'">
<div class="progress-bar">
<div :style="{ width: uploadState.progress + '%' }"></div>
</div>
<p>{{ uploadState.progress }}% - {{ formatSpeed(uploadState.speed) }}</p>
<p>Remaining: {{ uploadState.remainingTime }}s</p>
<button @click="cancelUpload">Cancel</button>
</div>
<div z-if="uploadState.status === 'completed'">
Upload complete! ✓
</div>
<div z-if="uploadState.status === 'error'">
Error: {{ uploadState.error }}
</div>
</div>
<script type="module">
createApp(() => {
const uploadState = reactive({
progress: 0,
status: 'idle',
speed: 0,
remainingTime: 0,
error: null
});
let abortFn = null;
async function handleFileSelect(e) {
const file = e.target.files[0];
if (!file) return;
const { promise, tracker, abort } = this.$upload('/upload', file);
// Sync tracker state with local state
Object.assign(uploadState, tracker);
abortFn = abort;
try {
await promise;
} catch (err) {
// Error already handled via tracker
}
}
function cancelUpload() {
if (abortFn) abortFn();
}
function formatSpeed(bytesPerSec) {
return (bytesPerSec / 1024).toFixed(2) + ' KB/s';
}
return { uploadState, handleFileSelect, cancelUpload, formatSpeed };
}).use(ZogHttpPlugin).mount('#app');
</script>
Download files with progress tracking and optional auto-save.
const { promise, tracker, abort } = this.$http.download('/files/report.pdf', {
filename: 'report.pdf', // Auto-triggers download
onProgress: (info) => {
console.log(`Downloaded: ${info.progress}%`);
},
onComplete: ({ blob, filename, size }) => {
console.log(`Downloaded ${filename} (${size} bytes)`);
}
});
const { blob } = await promise;
const { promise } = this.$http.download('/files/image.jpg');
const { blob } = await promise;
// Process blob manually
const imageUrl = URL.createObjectURL(blob);
The plugin provides reactive global state:
// Access in JavaScript
this.$http.state.loading // true when any request is pending
this.$http.state.error // Last error message
this.$http.state.pendingRequests // Number of pending requests
this.$http.state.lastRequest // Info about last request
// Access in template
<div z-show="$http.state.loading">Loading...</div>
<div z-if="$http.state.pendingRequests > 0">
{{ $http.state.pendingRequests }} requests in progress
</div>
const { requestId } = this.$http.upload('/upload', file);
// Cancel by ID
this.$http.cancelRequest(requestId);
// Cancel all pending requests
this.$http.cancelAll();
try {
await this.$http.get('/users');
} catch (error) {
if (error.isHttpError) {
console.log(error.status); // 404, 500, etc.
console.log(error.message); // Error message
console.log(error.response); // Full response object
console.log(error.request); // Original request info
}
}
import { HttpStatus } from './zog-http.js';
if (error.status === HttpStatus.NOT_FOUND) {
// Handle 404
}
if (error.status === HttpStatus.UNAUTHORIZED) {
// Handle 401
}
Create multiple HTTP clients with different configurations:
// Create a new instance
const adminApi = this.$http.create({
baseURL: 'https://admin.api.com',
headers: { 'X-Admin-Key': 'secret' }
});
await adminApi.get('/dashboard');
Use without Zog.js:
import { createHttpClient } from './zog-http.js';
const http = createHttpClient({
baseURL: 'https://api.example.com',
timeout: 30000
});
// Add interceptor
http.addRequestInterceptor(config => {
config.headers['Authorization'] = 'Bearer token';
return config;
});
// Make requests
const users = await http.get('/users');
<!DOCTYPE html>
<html>
<head>
<title>ZogHttp Demo</title>
<style>
.loading { opacity: 0.5; pointer-events: none; }
.progress-bar { width: 100%; height: 20px; background: #eee; }
.progress-fill { height: 100%; background: #4caf50; transition: width 0.3s; }
.error { color: red; }
</style>
</head>
<body>
<div id="app">
<!-- Global loading indicator -->
<div z-show="$http.state.loading" class="loading-overlay">
Loading...
</div>
<!-- User list -->
<section>
<h2>Users</h2>
<button @click="fetchUsers" :disabled="$http.state.loading">
Refresh Users
</button>
<ul>
<li z-for="user in users" :key="user.id">
{{ user.name }} ({{ user.email }})
<button @click="deleteUser(user.id)">Delete</button>
</li>
</ul>
</section>
<!-- Add user form -->
<section>
<h2>Add User</h2>
<input z-model="newUser.name" placeholder="Name" />
<input z-model="newUser.email" placeholder="Email" />
<button @click="addUser">Add User</button>
</section>
<!-- File upload -->
<section>
<h2>Upload Avatar</h2>
<input type="file" @change="handleUpload" accept="image/*" />
<div z-if="upload.status === 'uploading'">
<div class="progress-bar">
<div class="progress-fill" :style="{ width: upload.progress + '%' }"></div>
</div>
<span>{{ upload.progress }}%</span>
<button @click="cancelUpload">Cancel</button>
</div>
<div z-if="upload.status === 'completed'" style="color: green;">
Upload complete! ✓
</div>
<div z-if="upload.status === 'error'" class="error">
{{ upload.error }}
</div>
</section>
<!-- Error display -->
<div z-if="error" class="error">
{{ error }}
</div>
</div>
<script type="module">
import { createApp, ref, reactive } from './zog.js';
import { ZogHttpPlugin } from './zog-http.js';
createApp(() => {
// Reactive data
const users = reactive([]);
const newUser = reactive({ name: '', email: '' });
const error = ref('');
const upload = reactive({
progress: 0,
status: 'idle',
error: null
});
let abortUpload = null;
// Fetch users
async function fetchUsers() {
try {
error.value = '';
const response = await this.$http.get('/users');
users.splice(0, users.length, ...response.data);
} catch (e) {
error.value = e.message;
}
}
// Add user
async function addUser() {
if (!newUser.name || !newUser.email) return;
try {
error.value = '';
const response = await this.$http.post('/users', {
name: newUser.name,
email: newUser.email
});
users.push(response.data);
newUser.name = '';
newUser.email = '';
} catch (e) {
error.value = e.message;
}
}
// Delete user
async function deleteUser(id) {
try {
error.value = '';
await this.$http.delete(`/users/${id}`);
const index = users.findIndex(u => u.id === id);
if (index > -1) users.splice(index, 1);
} catch (e) {
error.value = e.message;
}
}
// Handle file upload
function handleUpload(event) {
const file = event.target.files[0];
if (!file) return;
const { promise, tracker, abort } = this.$upload('/avatar', file, {
fieldName: 'avatar',
additionalData: { userId: 1 },
onProgress: (info) => {
upload.progress = info.progress;
},
onComplete: () => {
upload.status = 'completed';
},
onError: (err) => {
upload.status = 'error';
upload.error = err.message;
}
});
upload.status = 'uploading';
upload.progress = 0;
upload.error = null;
abortUpload = abort;
}
// Cancel upload
function cancelUpload() {
if (abortUpload) {
abortUpload();
upload.status = 'idle';
}
}
return {
users,
newUser,
error,
upload,
fetchUsers,
addUser,
deleteUser,
handleUpload,
cancelUpload
};
})
.use(ZogHttpPlugin, {
baseURL: 'https://jsonplaceholder.typicode.com',
timeout: 30000,
retries: 2,
retryDelay: 1000,
})
.mount('#app');
</script>
</body>
</html>
The plugin is written in vanilla JavaScript but includes JSDoc comments for IDE support. For full TypeScript support, type definitions can be added:
interface HttpResponse<T = any> {
data: T;
status: number;
statusText: string;
headers: Record<string, string>;
config: RequestConfig;
request: RequestInfo;
}
interface UploadProgress {
loaded: number;
total: number;
progress: number;
speed: number;
remainingTime: number;
}
interface UploadOptions {
fieldName?: string;
additionalData?: Record<string, any>;
headers?: Record<string, string>;
onProgress?: (info: UploadProgress) => void;
onComplete?: (response: HttpResponse) => void;
onError?: (error: HttpError) => void;
}
Requires: fetch, AbortController, FormData, Blob, URL.createObjectURL
MIT License - feel free to use in any project.
FAQs
Powerful HTTP client plugin for Zog.js - Full-featured request library with interceptors, file upload/download progress tracking, and reactive state management
We found that @zogjs/http demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.

Security News
Open source is under attack because of how much value it creates. It has been the foundation of every major software innovation for the last three decades. This is not the time to walk away from it.

Security News
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.