@exceptionless/fetchclient@0.44.0Built and signed on GitHub ActionsBuilt and signed on GitHub Actions
Built and signed on GitHub Actions
latest
FoundatioFx/FetchClientWorks with
•JSR Score100%•This package works with Cloudflare Workers, Node.js, Deno, Bun, Browsers




Downloads192/wk
•Published6 months ago (0.44.0)
Fetch helper wrapper that supports middleware and the problem details standard
FetchClient

FetchClient is a library that makes it easier to use the fetch API for JSON APIs. It provides the following features:
- Typed Response - Full TypeScript support with strongly typed responses
- Functional - Standalone functions for simple usage
- Model Validator - Built-in validation with Problem Details support
- Caching - Response caching with TTL and programmatic invalidation
- Middleware - Extensible middleware pipeline for request/response handling
- Rate Limiting - Built-in rate limiting with per-domain support
- Request Timeout - Configurable timeouts with AbortSignal support
- Error Handling - Comprehensive error handling with Problem Details
- Authentication - Built-in Bearer token support
- Base URL - Global base URL configuration
- Loading State - Track request loading state with events
Install
npm install --save @exceptionless/fetchclient
Docs
Usage
Typed Response
import { FetchClient } from '@exceptionless/fetchclient'; type Products = { products: Array<{ id: number; name: string }>; }; const client = new FetchClient(); const response = await client.getJSON<Products>( `https://dummyjson.com/products/search?q=iphone&limit=10`, ); const products = response.data;
Functional
import { postJSON, getJSON } from '@exceptionless/fetchclient'; type Product = { id: number; title: string }; type Products = { products: Product[] }; const response = await postJSON<Product>( "https://dummyjson.com/products/add", { name: "iPhone 13", }, ); const product = await getJSON<Product>( `https://dummyjson.com/products/${response.data!.id}`, );
Model Validator
import { FetchClient, setModelValidator } from '@exceptionless/fetchclient'; setModelValidator(async (data: object | null) => { // use zod or any other validator const problem = new ProblemDetails(); const d = data as { password: string }; if (d?.password?.length < 6) { problem.errors.password = [ "Password must be longer than or equal to 6 characters.", ]; } return problem; }); const client = new FetchClient(); const data = { email: "test@test", password: "test", }; const response = await client.postJSON( "https://jsonplaceholder.typicode.com/todos/1", data, ); if (!response.ok) { // check response problem console.log(response.problem.detail); }
Caching
import { FetchClient } from '@exceptionless/fetchclient'; type Todo = { userId: number; id: number; title: string; completed: boolean }; const client = new FetchClient(); const response = await client.getJSON<Todo>( `https://jsonplaceholder.typicode.com/todos/1`, { cacheKey: ["todos", "1"], cacheDuration: 1000 * 60, // expires in 1 minute } ); // invalidate programmatically client.cache.delete(["todos", "1"]);
Middleware
import { FetchClient, useMiddleware } from '@exceptionless/fetchclient'; type Products = { products: Array<{ id: number; name: string }>; }; useMiddleware(async (ctx, next) => { console.log('starting request') await next(); console.log('completed request') }); const client = new FetchClient(); const response = await client.getJSON<Products>( `https://dummyjson.com/products/search?q=iphone&limit=10`, );
Rate Limiting
import { FetchClient, useRateLimit } from '@exceptionless/fetchclient'; // Enable rate limiting globally with 100 requests per minute useRateLimit({ maxRequests: 100, windowSeconds: 60, }); const client = new FetchClient(); const response = await client.getJSON( `https://api.example.com/data`, );
Request Timeout
import { FetchClient } from '@exceptionless/fetchclient'; const client = new FetchClient(); // Set timeout for individual requests const response = await client.getJSON( `https://api.example.com/data`, { timeout: 5000 } // 5 seconds ); // Use AbortSignal for cancellation const controller = new AbortController(); setTimeout(() => controller.abort(), 1000); const response2 = await client.getJSON( `https://api.example.com/data`, { signal: controller.signal } );
Error Handling
import { FetchClient } from '@exceptionless/fetchclient'; const client = new FetchClient(); try { const response = await client.getJSON(`https://api.example.com/data`); } catch (error) { // Handle HTTP errors (4xx, 5xx) if (error.problem) { console.log(error.problem.title); console.log(error.problem.detail); } } // Or handle specific status codes const response = await client.getJSON( `https://api.example.com/data`, { expectedStatusCodes: [404, 500], errorCallback: (response) => { if (response.status === 404) { console.log('Resource not found'); return true; // Don't throw } } } );
Authentication
import { FetchClient, setAccessTokenFunc } from '@exceptionless/fetchclient'; // Set global access token function setAccessTokenFunc(() => localStorage.getItem('token')); const client = new FetchClient(); const response = await client.getJSON(`https://api.example.com/data`); // Automatically adds Authorization: Bearer <token> header
Base URL
import { FetchClient, setBaseUrl } from '@exceptionless/fetchclient'; // Set global base URL setBaseUrl('https://api.example.com'); const client = new FetchClient(); const response = await client.getJSON(`/users/123`); // Requests to https://api.example.com/users/123
Loading State
import { FetchClient } from '@exceptionless/fetchclient'; const client = new FetchClient(); // Track loading state client.loading.on((isLoading) => { console.log(`Loading: ${isLoading}`); }); // Check current loading state console.log(client.isLoading); console.log(client.requestCount);
Also, take a look at the tests:
Contributing
Run tests:
deno run test
Lint code:
deno lint
Format code:
deno fmt
Type check code:
deno run check
License
MIT © Exceptionless
Built and signed on
GitHub Actions
Add Package
deno add jsr:@exceptionless/fetchclient
Import symbol
import * as fetchclient from "@exceptionless/fetchclient";
Import directly with a jsr specifier
import * as fetchclient from "jsr:@exceptionless/fetchclient";
Add Package
pnpm i jsr:@exceptionless/fetchclient
pnpm dlx jsr add @exceptionless/fetchclient
Import symbol
import * as fetchclient from "@exceptionless/fetchclient";
Add Package
yarn add jsr:@exceptionless/fetchclient
yarn dlx jsr add @exceptionless/fetchclient
Import symbol
import * as fetchclient from "@exceptionless/fetchclient";
Add Package
vlt install jsr:@exceptionless/fetchclient
Import symbol
import * as fetchclient from "@exceptionless/fetchclient";
Add Package
npx jsr add @exceptionless/fetchclient
Import symbol
import * as fetchclient from "@exceptionless/fetchclient";
Add Package
bunx jsr add @exceptionless/fetchclient
Import symbol
import * as fetchclient from "@exceptionless/fetchclient";