jiren
v3.1.0
Published
Jiren is a high-performance HTTP/HTTPS client, Faster than any other HTTP/HTTPS client.
Maintainers
Readme
Jiren 🚀
The fastest HTTP client for JavaScript - Simple, type-safe, and blazingly fast.
⚡ Performance: Fastest in the World
Jiren outperforms top clients in every metric—throughput, latency, and stability.
Concurrent Benchmark (mitata)
| Client | Avg Latency | Req/sec | vs Jiren | | ---------- | ----------- | ------------ | -------------- | | Jiren | 20.9 µs | 47,800/s | 🏆 Fastest | | Bun Fetch | 31.7 µs | 31,500/s | 52% slower | | Node-Fetch | 29.4 µs | 34,000/s | 41% slower | | Undici | 37.1 µs | 27,000/s | 77% slower | | Ky | 36.5 µs | 27,400/s | 75% slower | | Axios | 59.5 µs | 16,800/s | 185% slower | | Got | 61.3 µs | 16,300/s | 193% slower |
Sequential Benchmark
| Client | Avg Latency | Req/sec | vs Jiren | | ---------- | ----------- | ------------ | -------------- | | Jiren | 20.7 µs | 48,300/s | 🏆 Fastest | | Bun Fetch | 27.9 µs | 35,800/s | 35% slower | | Node-Fetch | 29.8 µs | 33,600/s | 44% slower | | Undici | 30.6 µs | 32,700/s | 48% slower | | Ky | 35.6 µs | 28,100/s | 72% slower | | Axios | 56.6 µs | 17,700/s | 173% slower | | Got | 59.9 µs | 16,700/s | 189% slower |
✨ Why Jiren?
| Feature | Benefit | | ----------------------- | ------------------------------------------------- | | ⚡ Blazing Fast | Proven to be the fastest JS HTTP client | | HTTP/3 Support | Automatic protocol upgrade for faster connections | | 💾 Built-in Caching | Automatic response caching with zero config | | � Progress Tracking | Real-time download progress with speed & ETA | | �📝 Type-Safe | Full TypeScript support with autocomplete | | 🔒 Anti-Bot Ready | Bypass common bot protections easily | | � Built-in Metrics | Track performance out of the box |
📦 Installation
bun add jiren🚀 Quick Start
Step 1: Create Your Client
import { JirenClient } from "jiren";
const client = new JirenClient({
urls: {
api: "https://api.example.com",
github: "https://api.github.com",
},
});Step 2: Make Requests
// GET request
const response = await client.url.api.get({ path: "/users" });
const users = await response.body.json();
// POST request
await client.url.api.post(JSON.stringify({ name: "John" }), {
path: "/users",
headers: { "Content-Type": "application/json" },
});That's it! 🎉
📖 Table of Contents
- Making Requests
- Response Handling
- Caching
- Progress Tracking
- Timeout Configuration
- Anti-Bot Protection
- Interceptors
- Metrics
- TypeScript Support
- API Reference
🌐 Making Requests
GET Requests
// Simple GET
const response = await client.url.api.get();
// GET with path
const user = await client.url.github.get({ path: "/users/octocat" });
// GET with headers
const data = await client.url.api.get({
path: "/protected",
headers: { Authorization: "Bearer token123" },
});POST, PUT, PATCH, DELETE
// POST
await client.url.api.post(JSON.stringify({ name: "Jane" }), {
path: "/users",
headers: { "Content-Type": "application/json" },
});
// PUT
await client.url.api.put(JSON.stringify({ name: "Updated" }), {
path: "/users/123",
});
// PATCH
await client.url.api.patch(JSON.stringify({ email: "[email protected]" }), {
path: "/users/123",
});
// DELETE
await client.url.api.delete(null, { path: "/users/123" });📤 Response Handling
Manual Parsing
const response = await client.url.api.get({ path: "/data" });
// Parse as JSON
const json = await response.body.json();
// Parse as text
const text = await response.body.text();
// Get as buffer
const buffer = await response.body.arrayBuffer();Auto-Parse (Recommended)
Let Jiren parse the response automatically:
// Auto-parse JSON
const users = await client.url.api.get({
path: "/users",
responseType: "json", // ← Returns parsed data directly!
});
// Auto-parse text
const html = await client.url.api.get({
path: "/page",
responseType: "text",
});🚀 Specific JSON Field Extraction
Extract specific fields from JSON responses with optimized native parsing:
// Extract only the fields you need (faster for large JSON)
const { id, status } = await client.url.api.getJsonFields<{
id: number;
status: string;
}>(["id", "status"]);
console.log(id, status); // Only these fields are extractedPerformance:
| Method | Use Case | Speed |
| -------------------------------- | -------------------- | ------------------------------ |
| response.body.json() | Need full object | Standard |
| getJsonFields(["field1", ...]) | Need specific fields | 2-4x faster for large JSON |
💡 Tip: Use
getJsonFields()when you have large JSON payloads but only need a few fields.
Response Properties
const response = await client.url.api.get({ path: "/users" });
console.log(response.status); // 200
console.log(response.ok); // true
console.log(response.headers); // { "content-type": "application/json", ... }
console.log(response.redirected); // false💾 Caching
Enable caching for instant responses on repeated requests:
Enable Caching
const client = new JirenClient({
urls: {
api: {
url: "https://api.example.com",
cache: true, // ← Enable caching (60s default)
},
},
});Custom Cache Duration
const client = new JirenClient({
urls: {
api: {
url: "https://api.example.com",
cache: { ttl: 300000 }, // 5 minutes
},
cdn: {
url: "https://cdn.example.com",
cache: { ttl: 3600000 }, // 1 hour
},
},
});Cache Performance
| Request Type | Speed | Improvement | | -------------- | ------ | ------------------ | | First request | ~150ms | - | | Cached request | ~1-2ms | 100x faster ⚡ |
🚀 Performance Benchmark
See the top of this README for detailed benchmark results. Jiren consistently wins on throughput and tail latency.
Refresh Cache
// Force refresh cached data
await client.url.api.prefetch({ path: "/users" });💡 Tip: Add
.cache/to your.gitignore
📊 Progress Tracking
Track download progress in real-time with the download() method:
Basic Usage
const response = await client.url.cdn.download({
path: "/large-file.zip",
onDownloadProgress: (progress) => {
console.log(`${progress.percent}% complete`);
console.log(`Speed: ${(progress.speed / 1024 / 1024).toFixed(2)} MB/s`);
console.log(`ETA: ${(progress.eta / 1000).toFixed(1)}s remaining`);
},
});
const data = await response.body.arrayBuffer();Progress Event Properties
| Property | Type | Description |
| --------- | -------- | ----------------------------- |
| loaded | number | Bytes transferred so far |
| total | number | Total bytes (0 if unknown) |
| percent | number | Percentage complete (0-100) |
| speed | number | Transfer speed in bytes/sec |
| eta | number | Estimated time remaining (ms) |
Progress Bar Example
await client.url.cdn.download({
path: "/video.mp4",
onDownloadProgress: (p) => {
const bar = "█".repeat(p.percent / 2).padEnd(50, "░");
process.stdout.write(`\r[${bar}] ${p.percent}%`);
},
});
console.log("\n✅ Download complete!");💡 Note: For HTTPS, progress events are fired as chunks are received. For HTTP, native streaming is used.
Upload Progress
Track upload progress in real-time with the upload() method:
await client.url.api.upload({
method: "POST",
path: "/upload",
body: largeData, // string or object
onUploadProgress: (progress) => {
console.log(`${progress.percent}% uploaded`);
console.log(`Speed: ${(progress.speed / 1024).toFixed(1)} KB/s`);
},
});Upload Progress Bar Example
await client.url.api.upload({
method: "PUT",
path: "/files/data.json",
body: JSON.stringify(bigPayload),
onUploadProgress: (p) => {
const bar = "█".repeat(p.percent / 2).padEnd(50, "░");
process.stdout.write(
`\r[${bar}] ${p.percent}% @ ${(p.speed / 1024 / 1024).toFixed(1)} MB/s`
);
},
});
console.log("\n✅ Upload complete!");💡 Performance: For HTTP, native Zig streaming achieves ~540 MB/s upload speeds. HTTPS fires initial (0%) and final (100%) events.
⏱️ Timeout Configuration
Set maximum wait time for requests to prevent hanging on slow or unresponsive servers:
Basic Usage
// Timeout after 5 seconds
const response = await client.url.api.get({
timeout: 5000, // milliseconds
});
// Also works with POST, PUT, etc.
await client.url.api.post({
body: { data: "value" },
timeout: 10000, // 10 second timeout
});Handling Timeout Errors
try {
const response = await client.url.api.get({
path: "/slow-endpoint",
timeout: 3000,
});
} catch (error) {
if (error.name === "TimeoutError") {
console.log("Request timed out!");
} else {
throw error;
}
}Behavior Notes
- Timeout is specified in milliseconds
- TimeoutError is thrown when the timeout is exceeded
- Timeout errors are not retried (even if retry is configured)
- If
timeoutis not set or is0, no timeout is applied
🔒 Anti-Bot Protection
Bypass Cloudflare and other bot protections:
const client = new JirenClient({
urls: {
protected: "https://protected-site.com",
},
antibot: true,
});
const response = await client.url.protected.get();🔄 Interceptors
Add middleware to modify requests/responses:
Add Authentication
const client = new JirenClient({
urls: { api: "https://api.example.com" },
interceptors: {
request: [
(ctx) => ({
...ctx,
headers: {
...ctx.headers,
Authorization: `Bearer ${getToken()}`,
},
}),
],
},
});Log Responses
const client = new JirenClient({
urls: { api: "https://api.example.com" },
interceptors: {
response: [
(ctx) => {
console.log(`${ctx.response.status} ${ctx.request.url}`);
return ctx;
},
],
},
});Handle Errors
const client = new JirenClient({
urls: { api: "https://api.example.com" },
interceptors: {
error: [
(error, ctx) => {
console.error(`Request failed: ${ctx.url}`);
},
],
},
});Add Interceptors Later
client.use({
request: [
(ctx) => ({ ...ctx, headers: { ...ctx.headers, "X-Custom": "value" } }),
],
});📊 Metrics
Track performance and cache efficiency:
Get Endpoint Metrics
const metrics = client.metrics.get("api");
console.log(metrics.requests.total); // Total requests made
console.log(metrics.timing.avgMs); // Average response time
console.log(metrics.cache.hitRate); // Cache hit percentageGet Global Metrics
const global = client.metrics.getGlobal();
console.log(global.totalRequests); // All requests
console.log(global.avgResponseTimeMs); // Average across all endpoints
console.log(global.overallCacheHitRate); // Overall cache performanceExport & Reset
// Export as JSON
const json = client.metrics.export();
// Reset metrics
client.metrics.reset();🔷 TypeScript Support
Type-Safe Responses
interface User {
id: number;
name: string;
email: string;
}
// TypeScript knows the response type!
const user = await client.url.api.get<User>({
path: "/users/123",
responseType: "json",
});
console.log(user.name); // ✅ Autocomplete works!Typed URL Keys
const client = new JirenClient({
urls: {
api: "https://api.example.com",
cdn: "https://cdn.example.com",
},
});
client.url.api.get(); // ✅ Valid
client.url.cdn.get(); // ✅ Valid
client.url.foo.get(); // ❌ TypeScript error!📚 API Reference
Creating a Client
new JirenClient({
urls: {
// Simple URL
api: "https://api.example.com",
// With caching
cdn: {
url: "https://cdn.example.com",
cache: true, // or { ttl: 60000 }
},
},
// Optional settings
antibot: false, // Enable anti-bot protection
benchmark: false, // Benchmark mode
interceptors: {}, // Request/response interceptors
});Request Methods
| Method | Signature |
| ----------------- | --------------------------------------------------------- |
| get() | get<T>(options?): Promise<Response<T>> |
| post() | post<T>(body?, options?): Promise<Response<T>> |
| put() | put<T>(body?, options?): Promise<Response<T>> |
| patch() | patch<T>(body?, options?): Promise<Response<T>> |
| delete() | delete<T>(body?, options?): Promise<Response<T>> |
| head() | head(options?): Promise<Response> |
| options() | options(options?): Promise<Response> |
| prefetch() | prefetch(options?): Promise<void> |
| download() | download<T>(options?): Promise<Response<T>> |
| getJsonFields() | getJsonFields<T>(fields, options?): Promise<Partial<T>> |
Request Options
{
path?: string; // URL path to append
headers?: Record<string, string>; // Request headers
responseType?: "json" | "text"; // Auto-parse response
maxRedirects?: number; // Max redirects (default: 5)
}Response Object
{
url: string;
status: number;
statusText: string;
headers: Record<string, string>;
ok: boolean;
redirected: boolean;
body: {
json<T>(): Promise<T>;
text(): Promise<string>;
arrayBuffer(): Promise<ArrayBuffer>;
blob(): Promise<Blob>;
};
}💡 Examples
API Client Pattern
// lib/api.ts
import { JirenClient } from "jiren";
export const api = new JirenClient({
urls: {
backend: {
url: process.env.API_URL!,
cache: { ttl: 30000 },
},
},
interceptors: {
request: [
(ctx) => ({
...ctx,
headers: {
...ctx.headers,
Authorization: `Bearer ${getSession()?.token}`,
},
}),
],
},
});
// Usage anywhere
import { api } from "@/lib/api";
const users = await api.url.backend.get({
path: "/users",
responseType: "json",
});React/Next.js Hook
function useApi<T>(path: string) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
api.url.backend
.get<T>({ path, responseType: "json" })
.then(setData)
.finally(() => setLoading(false));
}, [path]);
return { data, loading };
}
// Usage
function UserList() {
const { data: users, loading } = useApi<User[]>("/users");
if (loading) return <Spinner />;
return (
<ul>
{users?.map((u) => (
<li key={u.id}>{u.name}</li>
))}
</ul>
);
}🏗️ Framework Integration
Next.js (App Router)
To use Jiren in Next.js, add it to serverExternalPackages in next.config.ts:
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
// Required: Treat Jiren and Koffi as external native packages
serverExternalPackages: ["jiren", "koffi"],
};
export default nextConfig;Usage in API Routes:
// app/api/data/route.ts
import { JirenClient } from "jiren";
const client = new JirenClient({
urls: [{ key: "api", url: "https://api.example.com" }],
});
export async function GET() {
const response = await client.url.api.get({ path: "/users" });
return Response.json(await response.body.json());
}📋 Requirements
- Runtime:
- Bun v1.0.0+
- Node.js v18.0.0+ (macOS/Linux)
- OS: macOS (ARM64/x64) or Linux (x64)
📄 License
MIT © VK
🤝 Contributing
Contributions welcome! Please open an issue or submit a pull request.
