@friendlegends/fasthttp
v0.1.0
Published
A lightweight JavaScript HTTP client with TypeScript support that optimizes payloads, image uploads, and WebSocket messages before sending.
Maintainers
Readme
⚡ FastHTTP
Send normal requests. FastHTTP optimizes them before they leave your app.
A lightweight JavaScript HTTP client with first-class TypeScript support that optimizes JSON payloads, image uploads, and WebSocket messages automatically before sending.
Why FastHTTP?
FastHTTP is not an image compression utility. It is an HTTP/transport client with a built-in pre-send optimization pipeline. You write normal code — FastHTTP optimizes everything before it leaves.
Features
- 📦 Auto JSON optimization — trims strings, strips nulls, removes empty values
- 🖼️ Auto image optimization — resize, compress, format-convert during
api.post() - 🎯 Three-level config — global, per-route/per-request, and per-field/path rules
- 📋 Auto FormData — detects File/Blob → builds FormData automatically
- 🔌 WebSocket — auto-reconnect, event envelopes, message optimization
- ⚛️ React hooks —
useFastHTTP,useFastSocket - 📝 TypeScript generics — full type inference
- 📦 Dual format — ESM + CJS with
.d.tsdeclarations
Installation
npm install @friendlegends/fasthttp
# or
pnpm add @friendlegends/fasthttpQuick Start
JavaScript
import { createFastHTTP } from "@friendlegends/fasthttp";
const api = createFastHTTP({ baseURL: "https://api.example.com" });
const { data } = await api.post("/users", {
name: " Alice ", // → trimmed to "Alice"
bio: null, // → removed
});TypeScript
import { createFastHTTP } from "@friendlegends/fasthttp";
interface User { id: number; name: string; email: string; }
const api = createFastHTTP({ baseURL: "https://api.example.com" });
const { data } = await api.get<User>("/users/1");
console.log(data.name); // ✅ typedAutomatic Image Optimization
FastHTTP optimizes images automatically during api.post(), api.put(), and api.patch(). No manual optimizeImage(). No manual FormData.
import { createFastHTTP } from "@friendlegends/fasthttp";
const api = createFastHTTP({
baseURL: "/api",
optimize: { removeEmpty: true, trimStrings: true, debug: true },
image: {
enabled: true,
maxSizeKB: 300,
maxWidth: 1200,
maxHeight: 1200,
format: "image/webp",
},
});
await api.post("/profile", {
name: " Tend ", // → "Tend"
bio: null, // → removed
avatar: imageFile, // → compressed under 300 KB, converted to WebP
});What happens automatically:
- ✂️ JSON fields optimized (trim, remove nulls)
- 🖼️ Image File/Blob values compressed, resized, format-converted
- 📋 Body auto-converted to FormData
- 🚫 Content-Type NOT set manually (browser handles boundary)
Optimization Config Levels
FastHTTP supports three levels of config with clear precedence:
field/path > per-route/per-request > global
1. Global Config
const api = createFastHTTP({
baseURL: "/api",
optimize: { removeEmpty: true, trimStrings: true },
image: { enabled: true, maxSizeKB: 500 },
});2. Per-Route / Per-Request Override
// Different routes use different targets
await api.post("/profile", body, {
image: { enabled: true, maxSizeKB: 150 },
});
await api.post("/product", body, {
image: { enabled: true, maxSizeKB: 300 },
});
await api.post("/raw-upload", body, { skipOptimize: true });3. Per-Field / Path Rules
const api = createFastHTTP({
baseURL: "/api",
image: {
enabled: true,
maxSizeKB: 500,
fields: {
avatar: { maxSizeKB: 150, maxWidth: 512, maxHeight: 512, format: "image/webp" },
cover: { maxSizeKB: 900, maxWidth: 1920, format: "image/webp" },
"attachments.*": { enabled: false },
},
},
});
await api.post("/profile", {
avatar: avatarFile, // → 150 KB, 512×512, WebP
cover: coverFile, // → 900 KB, 1920px, WebP
attachments: [pdf, rawImg], // → NOT optimized
});Field path patterns supported:
avatar— matchesbody.avatarprofile.avatar— matchesbody.profile.avatarimages[]— matches every item inbody.imagesattachments.*— matches every item inbody.attachments
Target Size Behavior
maxSizeKBconverts to bytes internallymaxSizeBytestakes priority overmaxSizeKB- Iteratively reduces quality from
qualitydown tominQuality(default 0.1) - If quality alone isn't enough, reduces dimensions step-by-step (never upscales)
- Falls back to original file if optimization fails
Uploading Already-Optimized Images
Disable image optimization for a request to avoid double compression:
await api.post(
"/profile",
{ avatar: alreadyOptimizedFile },
{ image: { enabled: false } },
);Advanced: Manual optimizeImage()
For preview, comparison, or custom logic before upload:
import { createFastHTTP } from "@friendlegends/fasthttp";
import { optimizeImage } from "@friendlegends/fasthttp/image";
const api = createFastHTTP({ baseURL: "/api" });
const result = await optimizeImage(imageFile, {
maxSizeKB: 300,
maxWidth: 1200,
format: "image/webp",
});
console.log(`Saved ${result.savingsPercent}%`);
const previewUrl = URL.createObjectURL(result.blob);
// Upload with pipeline optimization disabled
await api.post(
"/profile",
{ avatar: result.file ?? result.blob },
{ image: { enabled: false } },
);WebSocket
import { createFastSocket } from "@friendlegends/fasthttp/ws";
const socket = createFastSocket({
url: "wss://api.example.com/ws",
autoReconnect: true,
optimize: { removeEmpty: true, trimStrings: true },
});
// Send auto-optimized message
socket.send({ text: " hello ", meta: null });
// → sent as: {"text":"hello"}
// Emit event envelope: { event: "chat:send", payload: { text: "hi" } }
socket.emit("chat:send", { text: " hi " });
// Listen for custom event envelopes
socket.on("chat:message", (payload) => console.log(payload));
// Skip optimization for one message
socket.send({ raw: true }, { skipOptimize: true });React Hooks
import { useFastHTTP, useFastSocket } from "@friendlegends/fasthttp/react";
function UserList() {
const { useRequest } = useFastHTTP({ baseURL: "https://api.example.com" });
const { data, loading, error, refetch } = useRequest<User[]>("/users");
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return <ul>{data?.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}Error Handling
import { createFastHTTP, FastHTTPError } from "@friendlegends/fasthttp";
try {
await api.get("/protected");
} catch (error) {
if (FastHTTPError.isFastHTTPError(error)) {
console.log(error.status); // 401
console.log(error.data); // { message: "Unauthorized" }
console.log(error.isTimeout); // false
console.log(error.isNetworkError); // false
}
}API Reference
createFastHTTP(config)
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| baseURL | string | required | Base URL for all requests |
| headers | Record<string, string> | {} | Default headers |
| timeout | number | 30000 | Timeout in ms |
| optimize | boolean \| OptimizeConfig | true | JSON optimization |
| image | ImageConfig | — | Image optimization |
| retries | number | 0 | Max retry attempts |
| retryDelay | number | 1000 | Retry delay ms |
Request Methods
| Method | Signature |
|--------|-----------|
| get | api.get<T>(url, options?) |
| post | api.post<T>(url, data?, options?) |
| put | api.put<T>(url, data?, options?) |
| patch | api.patch<T>(url, data?, options?) |
| delete | api.delete<T>(url, options?) |
Request Options (per-request override)
await api.post("/path", data, {
headers: { "X-Custom": "value" },
params: { page: 1 },
timeout: 5000,
signal: abortController.signal,
skipOptimize: true, // skip all optimization
optimize: { removeEmpty: true }, // override JSON optimization
image: { enabled: true, maxSizeKB: 200 }, // override image optimization
});Package Exports
| Import | Content |
|--------|---------|
| @friendlegends/fasthttp | createFastHTTP, optimizePayload, measureOptimization, buildFormData, FastHTTPError |
| @friendlegends/fasthttp/image | optimizeImage, optimizeImages |
| @friendlegends/fasthttp/ws | createFastSocket |
| @friendlegends/fasthttp/react | useFastHTTP, useFastSocket |
Roadmap
- [ ] Docusaurus documentation site
- [ ] Upload progress callbacks
- [ ] Batch request API
- [ ] Binary WebSocket protocol support
- [ ] Server-side image optimization (Node.js sharp integration)
- [ ] Request caching layer
Requirements
- Node.js 18+ or modern browser
- React 17+ (optional, only for hooks)
License
MIT © Friend Legends
