こんにちは!超軽量な Fetch ベースの HTTP クライアント「Z」へようこそ! ✨
Z は本当にシンプルで使いやすい HTTP クライアントです!TypeScript で書かれているので、型安全性もバッチリです!API との通信がもっと楽しくなること間違いなし!📱✨
主な特徴:
- 💪 TypeScript フルサポート
- 🪶 超軽量(依存関係ゼロ!)
- 🎯 シンプルで直感的な API
- ⚡ Fetch ベースで高速
- 🎛️ カスタマイズ可能なデフォルトオプション
- 🛑 AbortController サポート(商用利用に対応)
API のベース URL を設定しておくと、毎回フル URL を書く必要がなくなってスッキリ!
// Before 😫
const user = await fetch("https://api.example.com/users/1").then((r) =>
r.json()
);
const post = await fetch("https://api.example.com/posts/1").then((r) =>
r.json()
);
const comment = await fetch("https://api.example.com/comments/1")
.then((r) => r.json())
.catch((e) => console.error(e));
// After with Z 😊
const z = new Z("https://api.example.com");
const result = await z.get<User>("/users/1");
console.log(result.data.user); //* 上のはこれと同じです😊
const { data: user } = await z.get<User>("/users/1");
const { data: post } = await z.get<Post>("/posts/1");
const { data: comment } = await z.get<Comment>("/comments/1");認証トークンなどのヘッダーをデフォルトで設定できます:
const z = new Z("https://api.example.com", {
headers: {
Authorization: "Bearer your-token",
"X-Custom-Header": "custom-value",
},
});
// ヘッダーは自動的に付与されます!
const { data: user } = await z.get<User>("/me");TypeScript の型推論で API レスポンスも安全に!
interface User {
id: number;
name: string;
email: string;
}
interface CreateUserInput {
name: string;
email: string;
}
const z = new Z("https://api.example.com");
// レスポンスの型が保証されます!
const { data: user } = await z.get<User>("/users/1");
console.log(user.name); // 型補完が効きます!
// リクエストボディも型安全!
const { data: newUser } = await z.post<User>("/users", {
name: "たろう",
email: "taro@example.com",
});import Z from "@ptt/zz";
// クライアントの作成
const z = new Z("https://api.example.com", {
headers: { Authorization: "Bearer token" },
});
// GETリクエスト
interface Todo {
id: number;
title: string;
completed: boolean;
}
// 型安全!🎉
const { data: todo } = await z.get<Todo>("/todos/1");
console.log(todo.title); // TypeScriptの補完が効きます!
// POSTリクエスト
const { data: newTodo } = await z.post<Todo>("/todos", {
title: "新しいタスク",
completed: false,
});
// その他のメソッドも同様に!
await z.put<Todo>("/todos/1", { completed: true });
await z.patch<Todo>("/todos/1", { title: "更新されたタスク" });
await z.delete("/todos/1");- シンプル: 必要最小限の機能だけを持っているので、学習コストがほぼゼロ!
- 型安全: TypeScript の力を最大限に活用して、API レスポンスの型を完全にサポート!
- 軽量: 外部依存関係がないので、バンドルサイズを気にする必要なし!
- モダン: JavaScript の Fetch API をベースにしているので、モダンブラウザで快適に動作!
| 機能 | 素の Fetch | Z |
|---|---|---|
| ベース URL | ❌ 毎回フル URL を書く必要がある | ✅ ベース URL を一度設定すれば OK |
| デフォルトヘッダー | ❌ 毎回設定が必要 | ✅ コンストラクタで一度設定 |
| JSON 処理 | ❌ 手動で JSON.stringify() / .json() |
✅ 自動的に処理 |
| 型安全性 | ❌ レスポンスの型が any | ✅ TypeScript ジェネリクスで完全な型安全性 |
| エラーハンドリング | ❌ .ok チェックを毎回書く必要がある |
✅ 自動的にエラーをスロー |
| バンドルサイズ | ✅ 0KB(標準 API) | ✅ 約 1KB(最小限) |
// Fetch の場合 😫
const response = await fetch('https://api.example.com/users/1', {
headers: {
'Authorization': 'Bearer token',
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const user = await response.json();
// Z の場合 😊
const z = new Z('https://api.example.com', {
headers: { 'Authorization': 'Bearer token' }
});
const { data: user } = await z.get<User>('/users/1');| 機能 | Axios | Z |
|---|---|---|
| バンドルサイズ | ❌ 約 13KB(gzip 後) | ✅ 約 1KB |
| 依存関係 | ❌ 複数の依存関係あり | ✅ ゼロ依存 |
| ブラウザサポート | ✅ 広範なサポート(IE11 含む) | ✅ モダンブラウザ(Fetch API サポート必須) |
| TypeScript サポート | ✅ TypeScript ネイティブ | |
| AbortController | ✅ サポート | ✅ ネイティブサポート |
| インターセプター | ✅ リクエスト/レスポンスインターセプター | ❌ なし(シンプルさを優先) |
| 進捗イベント | ✅ アップロード/ダウンロード進捗 | ❌ なし(シンプルさを優先) |
| 自動リトライ | ❌ プラグインが必要 | ❌ なし(必要に応じて実装) |
Z が最適な場合:
- 🎯 シンプルな REST API クライアントが必要な場合
- 📦 バンドルサイズを最小限に抑えたい場合
- 🚀 モダンブラウザ専用のアプリケーション
- ✨ TypeScript で型安全性を重視する場合
- 🪶 依存関係を増やしたくない場合
Axios が最適な場合:
- 🔧 高度な機能(インターセプター、進捗トラッキング)が必要な場合
- 🌐 IE11 などの古いブラウザをサポートする必要がある場合
- 📊 複雑なリクエスト処理が必要な場合
npx jsr add @ptt/zzまずはJSONPlaceholderで簡単に試してみましょう!
interface Post {
userId: number;
id: number;
title: string;
body: string;
}
interface Comment {
postId: number;
id: number;
name: string;
email: string;
body: string;
}
// クライアントの初期化
const z = new Z("https://jsonplaceholder.typicode.com");
// 投稿を取得してみよう!
const { data: post } = await z.get<Post>("/posts/1");
console.log(`タイトル: ${post.title}`);
// コメントも取得できます!
const { data: comments } = await z.get<Comment[]>(`/posts/${post.id}/comments`);
console.log(`${comments.length}件のコメントがあります!`);
// 新しい投稿を作成
const { data: newPost } = await z.post<Post>("/posts", {
title: "新しい投稿!",
body: "Zを使うと簡単にAPIが叩けます!",
userId: 1,
});
console.log(`投稿が作成されました! ID: ${newPost.id}`);
// 投稿を更新
const { data: updatedPost } = await z.put<Post>(`/posts/${newPost.id}`, {
...newPost,
title: "更新された投稿!",
});
console.log(`タイトルを更新しました!: ${updatedPost.title}`);
// 投稿を削除
await z.delete(`/posts/${newPost.id}`);
console.log("投稿を削除しました!");実際に試してみると、こんな感じの結果が得られます:
タイトル: sunt aut facere repellat provident occaecati excepturi optio reprehenderit
10件のコメントがあります!
投稿が作成されました! ID: 101
タイトルを更新しました!: 更新された投稿!
投稿を削除しました!try {
await z.get("/not-found");
} catch (error) {
console.error("エラーが発生しました!", error.message);
// 'あれ?通信でエラーが起きちゃった! 😢 (ステータス: 404, メッセージ: Not Found)'
}商用アプリケーションでは、ユーザーがページを離れた時やタイムアウト時にリクエストをキャンセルする必要があります。Z は AbortController を完全にサポートしています!
const z = new Z("https://api.example.com");
const controller = new AbortController();
// ボタンクリックなどでキャンセル可能に
document.getElementById("cancel-btn").addEventListener("click", () => {
controller.abort();
});
try {
const result = await z.get("/slow-endpoint", { signal: controller.signal });
console.log(result.data);
} catch (error) {
if (error.name === 'AbortError') {
console.log('リクエストがキャンセルされました!');
}
}import Z, { createTimeout } from "@ptt/zz";
const z = new Z("https://api.example.com");
// 5秒でタイムアウト
const controller = createTimeout(5000);
try {
const result = await z.get("/slow-endpoint", { signal: controller.signal });
console.log(result.data);
} catch (error) {
if (error.name === 'AbortError') {
console.log('リクエストがタイムアウトしました!');
}
}import { useEffect, useState } from 'react';
import Z from '@ptt/zz';
function UserProfile({ userId }: { userId: number }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const z = new Z("https://api.example.com");
const controller = new AbortController();
async function fetchUser() {
try {
const { data } = await z.get(`/users/${userId}`, {
signal: controller.signal
});
setUser(data);
} catch (error) {
if (error.name !== 'AbortError') {
console.error('Failed to fetch user:', error);
}
} finally {
setLoading(false);
}
}
fetchUser();
// コンポーネントがアンマウントされたらリクエストをキャンセル
return () => controller.abort();
}, [userId]);
if (loading) return <div>読み込み中...</div>;
return <div>{user?.name}</div>;
}楽しい API ライフを! 🎈
より詳しい情報はGitHub リポジトリをチェックしてね! 🌟
Welcome to Z, an ultra-lightweight Fetch-based HTTP client! ✨
Z is a simple and easy-to-use HTTP client written in TypeScript for complete type safety. Making API communication fun and reliable!
Key features:
- 💪 Full TypeScript support - Type-safe requests and responses
- 🪶 Ultra-lightweight - Zero dependencies!
- 🎯 Simple & intuitive API - Easy to learn and use
- ⚡ Fast - Built on the modern Fetch API
- 🎛️ Customizable defaults - Set base URLs and default headers
- 🛑 AbortController support - Production-ready request cancellation
npx jsr add @ptt/zzimport Z from "@ptt/zz";
// Create a client
const z = new Z("https://api.example.com", {
headers: { Authorization: "Bearer token" },
});
// Make a GET request
interface Todo {
id: number;
title: string;
completed: boolean;
}
const { data: todo } = await z.get<Todo>("/todos/1");
console.log(todo.title); // TypeScript autocomplete works!
// Make a POST request
const { data: newTodo } = await z.post<Todo>("/todos", {
title: "New task",
completed: false,
});
// Other methods work similarly
await z.put<Todo>("/todos/1", { completed: true });
await z.patch<Todo>("/todos/1", { title: "Updated task" });
await z.delete("/todos/1");For commercial applications, you often need to cancel requests when users navigate away or implement timeouts. Z fully supports AbortController!
const z = new Z("https://api.example.com");
const controller = new AbortController();
// Cancel on button click
document.getElementById("cancel-btn").addEventListener("click", () => {
controller.abort();
});
try {
const result = await z.get("/slow-endpoint", { signal: controller.signal });
console.log(result.data);
} catch (error) {
if (error.name === 'AbortError') {
console.log('Request was cancelled!');
}
}import Z, { createTimeout } from "@ptt/zz";
const z = new Z("https://api.example.com");
// 5-second timeout
const controller = createTimeout(5000);
try {
const result = await z.get("/slow-endpoint", { signal: controller.signal });
console.log(result.data);
} catch (error) {
if (error.name === 'AbortError') {
console.log('Request timed out!');
}
}import { useEffect, useState } from 'react';
import Z from '@ptt/zz';
function UserProfile({ userId }: { userId: number }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const z = new Z("https://api.example.com");
const controller = new AbortController();
async function fetchUser() {
try {
const { data } = await z.get(`/users/${userId}`, {
signal: controller.signal
});
setUser(data);
} catch (error) {
if (error.name !== 'AbortError') {
console.error('Failed to fetch user:', error);
}
} finally {
setLoading(false);
}
}
fetchUser();
// Cancel request when component unmounts
return () => controller.abort();
}, [userId]);
if (loading) return <div>Loading...</div>;
return <div>{user?.name}</div>;
}- Simple: Minimal API surface - almost zero learning curve!
- Type-safe: Leverage TypeScript's full power for API response types
- Lightweight: No external dependencies means smaller bundle size
- Modern: Built on JavaScript's Fetch API for optimal browser support
- Production-ready: AbortController support for commercial applications
| Feature | Native Fetch | Z |
|---|---|---|
| Base URL | ❌ Must write full URL every time | ✅ Set once in constructor |
| Default Headers | ❌ Must set for each request | ✅ Set once in constructor |
| JSON Handling | ❌ Manual JSON.stringify() / .json() |
✅ Automatic |
| Type Safety | ❌ Response type is any | ✅ Full type safety with generics |
| Error Handling | ❌ Must check .ok every time |
✅ Automatically throws on error |
| Bundle Size | ✅ 0KB (standard API) | ✅ ~1KB (minimal) |
// With Fetch 😫
const response = await fetch('https://api.example.com/users/1', {
headers: {
'Authorization': 'Bearer token',
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const user = await response.json();
// With Z 😊
const z = new Z('https://api.example.com', {
headers: { 'Authorization': 'Bearer token' }
});
const { data: user } = await z.get<User>('/users/1');| Feature | Axios | Z |
|---|---|---|
| Bundle Size | ❌ ~13KB (gzipped) | ✅ ~1KB |
| Dependencies | ❌ Multiple dependencies | ✅ Zero dependencies |
| Browser Support | ✅ Wide support (including IE11) | ✅ Modern browsers (Fetch API required) |
| TypeScript Support | ✅ Native TypeScript | |
| AbortController | ✅ Supported | ✅ Native support |
| Interceptors | ✅ Request/Response interceptors | ❌ None (simplicity first) |
| Progress Events | ✅ Upload/Download progress | ❌ None (simplicity first) |
| Auto Retry | ❌ Requires plugin | ❌ None (implement if needed) |
Z is best when you need:
- 🎯 Simple REST API client without complexity
- 📦 Minimal bundle size for better performance
- 🚀 Modern browser-only applications
- ✨ Type safety with TypeScript
- 🪶 Zero dependencies in your project
Axios is best when you need:
- 🔧 Advanced features (interceptors, progress tracking)
- 🌐 Old browser support (IE11, etc.)
- 📊 Complex request processing with transformations
new Z(baseUrl?: string, options?: RequestInit)baseUrl: Base URL for all requests (e.g., "https://api.example.com")options: Default Fetch options applied to all requests
All methods support the signal option for request cancellation via AbortController.
Execute a GET request.
Execute a POST request with JSON body.
Execute a PUT request with JSON body.
Execute a PATCH request with JSON body.
Execute a DELETE request.
Creates an AbortController that automatically aborts after the specified timeout.
const controller = createTimeout(5000); // 5 seconds
await z.get("/endpoint", { signal: controller.signal });try {
await z.get("/not-found");
} catch (error) {
console.error("Error occurred!", error.message);
}MIT
Contributions are welcome! Check out the GitHub repository.