fetchnix
v1.0.1
Published
A lightweight, type-safe Fetch client for modern JavaScript and TypeScript.
Maintainers
Readme
Fetchnix
A lightweight, type-safe Fetch client for modern JavaScript and TypeScript.
Installation • Quick Start • Features • API • Errors • Configuration • Documentation • Changelog
Contents
- About
- Features
- Installation
- Quick Start
- HTTP Methods
- Query Parameters
- JSON Requests
- Timeouts
- Retries
- Retry-After
- Abort Requests
- Errors
- Configuration
- TypeScript
- Browser & Node.js
- API
- Response Parsing
- Documentation
- Changelog
- Development
- License
About
Fetchnix is a lightweight wrapper around the native Fetch API.
It keeps the familiar Fetch model while providing a small set of useful request utilities:
- Type-safe generic responses
- Query parameter handling
- Automatic JSON request serialization
- Automatic response parsing
- Request timeouts
- Configurable retries
- Exponential retry backoff
Retry-Aftersupport- Request cancellation
- Typed HTTP errors
- Zero runtime dependencies
Fetchnix does not replace the native Fetch API. It provides a small API layer on top of it.
Features
- Zero runtime dependencies
- TypeScript-first
- GET, POST, PUT, PATCH and DELETE
- Generic response types
- Query parameter support
- Automatic JSON serialization
- Automatic JSON and text response parsing
- Configurable request timeout
- Configurable retry behavior
- Retryable HTTP status configuration
- Exponential retry backoff
- Retry jitter
Retry-Aftersupport- AbortController support
- Detailed typed errors
- Browser and Node.js support
- ESM and CommonJS builds
- Small API surface
Installation
Using npm:
npm install fetchnixUsing pnpm:
pnpm add fetchnixUsing yarn:
yarn add fetchnixQuick Start
import { fetchnix } from "fetchnix";
interface User {
id: number;
name: string;
}
const user = await fetchnix.get<User>(
"https://api.example.com/users/1"
);
console.log(user.id);
console.log(user.name);HTTP Methods
GET
const users = await fetchnix.get<User[]>(
"https://api.example.com/users"
);POST
const user = await fetchnix.post<User>(
"https://api.example.com/users",
{
name: "Budi"
}
);PUT
const user = await fetchnix.put<User>(
"https://api.example.com/users/1",
{
name: "Budi Updated"
}
);PATCH
const user = await fetchnix.patch<User>(
"https://api.example.com/users/1",
{
name: "Budi Updated"
}
);DELETE
await fetchnix.delete(
"https://api.example.com/users/1"
);Query Parameters
Query parameters can be passed through the params option.
const users = await fetchnix.get<User[]>(
"https://api.example.com/users",
{
params: {
page: 1,
limit: 20,
active: true
}
}
);The resulting request includes:
?page=1&limit=20&active=trueExisting query parameters in the URL are preserved.
JSON Requests
Plain JavaScript objects are automatically serialized as JSON.
const user = await fetchnix.post<User>(
"https://api.example.com/users",
{
name: "Budi",
age: 17
}
);When no Content-Type header is provided, Fetchnix automatically adds:
Content-Type: application/jsonNative request bodies are passed through without JSON serialization, including:
FormDataBlobURLSearchParamsArrayBuffer- Typed arrays
ReadableStream- Strings
Custom headers are also supported:
const user = await fetchnix.post<User>(
"https://api.example.com/users",
{
name: "Budi"
},
{
headers: {
Authorization: "Bearer token"
}
}
);Timeouts
Set a timeout in milliseconds:
const user = await fetchnix.get<User>(
"https://api.example.com/users/1",
{
timeout: 5000
}
);The timeout applies independently to each request attempt.
Timeout failures can be handled using FetchnixTimeoutError:
import {
fetchnix,
FetchnixTimeoutError
} from "fetchnix";
try {
await fetchnix.get<User>(
"https://api.example.com/users/1",
{
timeout: 5000
}
);
} catch (error) {
if (
error instanceof FetchnixTimeoutError
) {
console.log(error.timeout);
console.log(error.url);
console.log(error.method);
}
}Retries
Fetchnix supports configurable retries for temporary HTTP failures.
const data = await fetchnix.get(
"https://api.example.com/data",
{
retry: 3,
retryDelay: 1000
}
);The default retryable status codes are:
408
429
500
502
503
504Custom retry status codes can be provided:
const data = await fetchnix.get(
"https://api.example.com/data",
{
retry: 3,
retryStatusCodes: [
429,
500,
503
]
}
);Fetchnix uses exponential backoff with jitter between retry attempts.
The retry value represents the number of retries after the initial request.
For example:
{
retry: 3
}allows up to four total request attempts.
Retry-After
When a retryable HTTP response contains a Retry-After header, Fetchnix uses it when calculating the retry delay.
For example:
Retry-After: 5A maximum retry delay can be configured:
const data = await fetchnix.get(
"https://api.example.com/data",
{
retry: 5,
maxRetryDelay: 30000
}
);Retry-After values expressed as seconds and HTTP dates are supported.
Abort Requests
Fetchnix supports the standard AbortController API.
const controller =
new AbortController();
const request = fetchnix.get(
"https://api.example.com/data",
{
signal: controller.signal
}
);
controller.abort();
await request;An externally aborted request throws FetchnixAbortError.
import {
fetchnix,
FetchnixAbortError
} from "fetchnix";
const controller =
new AbortController();
try {
await fetchnix.get(
"https://api.example.com/data",
{
signal: controller.signal
}
);
} catch (error) {
if (
error instanceof FetchnixAbortError
) {
console.log("Request cancelled");
}
}External cancellation is not retried.
Errors
Fetchnix provides dedicated error classes for HTTP, timeout, and cancellation failures.
FetchnixError
FetchnixError is thrown when the server returns a non-success HTTP status after retry handling has completed.
import {
fetchnix,
FetchnixError
} from "fetchnix";
try {
await fetchnix.get(
"https://api.example.com/users/1"
);
} catch (error) {
if (
error instanceof FetchnixError
) {
console.log(error.status);
console.log(error.statusText);
console.log(error.data);
console.log(error.url);
console.log(error.method);
}
}Available properties:
| Property | Type |
|---|---|
| status | number |
| statusText | string |
| data | unknown \| null |
| response | Response |
| url | string |
| method | string |
FetchnixTimeoutError
import {
FetchnixTimeoutError
} from "fetchnix";
if (
error instanceof FetchnixTimeoutError
) {
console.log(error.timeout);
console.log(error.url);
console.log(error.method);
}Available properties:
| Property | Type |
|---|---|
| timeout | number |
| url | string |
| method | string |
FetchnixAbortError
import {
FetchnixAbortError
} from "fetchnix";
if (
error instanceof FetchnixAbortError
) {
console.log(error.url);
console.log(error.method);
}Available properties:
| Property | Type |
|---|---|
| url | string |
| method | string |
Configuration
Fetchnix extends the standard RequestInit options with additional request controls.
| Option | Type | Default | Description |
|---|---|---:|---|
| params | Record<string, ...> | undefined | Query parameters |
| timeout | number | undefined | Timeout per attempt in milliseconds |
| retry | number | 0 | Number of retries |
| retryDelay | number | 1000 | Base retry delay |
| retryStatusCodes | number[] | [408,429,500,502,503,504] | Retryable HTTP statuses |
| maxRetryDelay | number | 30000 | Maximum retry delay |
| signal | AbortSignal \| null | undefined | External cancellation signal |
| headers | HeadersInit | undefined | Request headers |
All other standard RequestInit options can be passed directly.
Example:
const data = await fetchnix.get(
"https://api.example.com/data",
{
headers: {
Authorization: "Bearer token",
Accept: "application/json"
},
credentials: "include",
cache: "no-store"
}
);TypeScript
Fetchnix is written in TypeScript and provides declaration files with the package.
Generic response types can be supplied to every HTTP method.
interface Product {
id: number;
name: string;
price: number;
}
const product =
await fetchnix.get<Product>(
"https://api.example.com/products/1"
);
console.log(product.id);
console.log(product.name);
console.log(product.price);For collections:
interface Product {
id: number;
name: string;
}
const products =
await fetchnix.get<Product[]>(
"https://api.example.com/products"
);
products.forEach((product) => {
console.log(product.name);
});Generic types describe the expected response shape. They do not perform runtime validation.
Browser & Node.js
Fetchnix uses the native Fetch API and does not include its own HTTP implementation.
It can be used in environments that provide the required Fetch APIs, including:
- Modern browsers
- Node.js 18+
- TypeScript applications
- Frontend applications
- Server-side applications
- CLI tools
The package provides both ESM and CommonJS builds.
API
fetchnix.get<T>()
fetchnix.get<T>(
url,
options?
)Performs a GET request.
fetchnix.post<T>()
fetchnix.post<T>(
url,
body?,
options?
)Performs a POST request.
fetchnix.put<T>()
fetchnix.put<T>(
url,
body?,
options?
)Performs a PUT request.
fetchnix.patch<T>()
fetchnix.patch<T>(
url,
body?,
options?
)Performs a PATCH request.
fetchnix.delete<T>()
fetchnix.delete<T>(
url,
options?
)Performs a DELETE request.
Response Parsing
Fetchnix automatically parses responses according to the response Content-Type.
For JSON responses, the body is parsed using JSON.parse().
For non-JSON responses, the body is returned as text.
Empty responses return null.
204 No Content and 205 Reset Content responses return:
nullIf a response declares JSON but contains invalid JSON, Fetchnix returns the response body as text instead of throwing a JSON parsing error.
Request Bodies
Fetchnix automatically serializes plain objects:
await fetchnix.post(
"https://api.example.com/users",
{
name: "Budi",
age: 17
}
);Native body types are passed directly to fetch():
const form =
new FormData();
form.append(
"username",
"budi"
);
await fetchnix.post(
"https://api.example.com/users",
form
);Other native body types such as Blob, URLSearchParams, ArrayBuffer, typed arrays, strings, and ReadableStream are also preserved.
Documentation
Repository:
Package:
For the complete release history, see:
Changelog
See the complete changelog:
Development
Clone the repository:
git clone https://github.com/LivvSKy/fetchnix.git
cd fetchnixInstall development dependencies:
npm installRun TypeScript type checking:
npm run typecheckBuild the package:
npm run buildPreview the files that will be included in the npm package:
npm pack --dry-runThe compiled package is generated in:
dist/License
MIT
