{"id":26590365,"url":"https://github.com/smooai/fetch","last_synced_at":"2026-05-07T13:04:23.239Z","repository":{"id":283742971,"uuid":"950967948","full_name":"SmooAI/fetch","owner":"SmooAI","description":"A powerful HTTP client library with built-in support for retries, timeouts, rate limiting, and circuit breaking. Designed for both Node.js and browser environments, with seamless integration with AWS Lambda and structured logging.","archived":false,"fork":false,"pushed_at":"2025-03-21T22:02:23.000Z","size":16,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-21T22:29:14.413Z","etag":null,"topics":["fetch","typescript"],"latest_commit_sha":null,"homepage":"https://smoo.ai","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SmooAI.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-03-19T00:50:08.000Z","updated_at":"2025-03-21T22:02:25.000Z","dependencies_parsed_at":"2025-03-21T22:29:24.635Z","dependency_job_id":"8de4323e-be4f-4e02-89d7-06aa270eb016","html_url":"https://github.com/SmooAI/fetch","commit_stats":null,"previous_names":["smooai/fetch"],"tags_count":0,"template":false,"template_full_name":"SmooAI/library-template","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SmooAI%2Ffetch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SmooAI%2Ffetch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SmooAI%2Ffetch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SmooAI%2Ffetch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SmooAI","download_url":"https://codeload.github.com/SmooAI/fetch/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245110769,"owners_count":20562439,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fetch","typescript"],"created_at":"2025-03-23T13:38:20.772Z","updated_at":"2026-05-07T13:04:23.231Z","avatar_url":"https://github.com/SmooAI.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- Improved compatibility of back to top link: See: https://github.com/othneildrew/Best-README-Template/pull/73 --\u003e\n\n\u003ca name=\"readme-top\"\u003e\u003c/a\u003e\n\n\u003c!--\n*** Thanks for checking out the Best-README-Template. If you have a suggestion\n*** that would make this better, please fork the repo and create a pull request\n*** or simply open an issue with the tag \"enhancement\".\n*** Don't forget to give the project a star!\n*** Thanks again! Now go create something AMAZING! :D\n--\u003e\n\n\u003c!-- PROJECT SHIELDS --\u003e\n\u003c!--\n*** I'm using markdown \"reference style\" links for readability.\n*** Reference links are enclosed in brackets [ ] instead of parentheses ( ).\n*** See the bottom of this document for the declaration of the reference variables\n*** for contributors-url, forks-url, etc. This is an optional, concise syntax you may use.\n*** https://www.markdownguide.org/basic-syntax/#reference-style-links\n--\u003e\n\n\u003c!-- PROJECT LOGO --\u003e\n\u003cbr /\u003e\n\u003cdiv align=\"center\"\u003e\n  \u003ca href=\"https://smoo.ai\"\u003e\n    \u003cimg src=\"images/logo.png\" alt=\"SmooAI Logo\" /\u003e\n  \u003c/a\u003e\n\u003c/div\u003e\n\n\u003c!-- ABOUT THE PROJECT --\u003e\n\n## About Smoo AI\n\n**[Smoo AI](https://smoo.ai)** is an AI platform that helps businesses multiply their customer, employee, and developer experience — conversational AI for support and sales, paired with the production-grade developer tooling we use to build it.\n\nThis library is part of a small family of open-source packages we maintain to keep our own stack honest: contextual logging, typed HTTP, file storage, and agent orchestration. Use them in your stack, or take them as a reference for how we build.\n\n- 🌐 [smoo.ai](https://smoo.ai) — the product\n- 📦 [smoo.ai/open-source](https://smoo.ai/open-source) — every open-source package we ship\n- 🐙 [github.com/SmooAI](https://github.com/SmooAI) — the source\n\n## About @smooai/fetch\n\n**Stop writing the same retry logic over and over** - A resilient HTTP client that handles the chaos of real-world APIs, so you can focus on building features instead of handling failures.\n\n![NPM Version](https://img.shields.io/npm/v/%40smooai%2Ffetch?style=for-the-badge)\n![NPM Downloads](https://img.shields.io/npm/dw/%40smooai%2Ffetch?style=for-the-badge)\n![NPM Last Update](https://img.shields.io/npm/last-update/%40smooai%2Ffetch?style=for-the-badge)\n\n![GitHub License](https://img.shields.io/github/license/SmooAI/fetch?style=for-the-badge)\n![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/SmooAI/fetch/release.yml?style=for-the-badge)\n![GitHub Repo stars](https://img.shields.io/github/stars/SmooAI/fetch?style=for-the-badge)\n\n### Multi-Language Support\n\n@smooai/fetch is available as native implementations in **TypeScript**, **Python**, **Rust**, and **Go** — each built with idiomatic patterns for its ecosystem.\n\n| Language   | Package                                                        | Install                                   |\n| ---------- | -------------------------------------------------------------- | ----------------------------------------- |\n| TypeScript | [`@smooai/fetch`](https://www.npmjs.com/package/@smooai/fetch) | `pnpm add @smooai/fetch`                  |\n| Python     | [`smooai-fetch`](https://pypi.org/project/smooai-fetch/)       | `pip install smooai-fetch`                |\n| Rust       | [`smooai-fetch`](https://crates.io/crates/smooai-fetch)        | `cargo add smooai-fetch`                  |\n| Go         | `github.com/SmooAI/fetch/go/fetch`                             | `go get github.com/SmooAI/fetch/go/fetch` |\n\nLanguage-specific source code lives in the [`python/`](./python/), [`rust/`](./rust/), and [`go/`](./go/) directories.\n\n### Why @smooai/fetch?\n\nEver had your app crash because an API was down for 2 seconds? Or watched your users stare at loading spinners because a third-party service hit its rate limit? Traditional fetch gives you the request, but leaves you to handle the reality of network failures.\n\n**@smooai/fetch automatically handles:**\n\n**For Unreliable APIs:**\n\n- 🔄 **Smart retries** - Exponential backoff with jitter to prevent thundering herds\n- ⏱️ **Automatic timeouts** - Never hang indefinitely on slow endpoints\n- 🚦 **Rate limit respect** - Reads Retry-After headers and backs off intelligently\n- 🔌 **Circuit breaking** - Stop hammering services that are clearly down\n- ⚡ **Request deduplication** - Prevent duplicate in-flight requests\n\n**For Developer Experience:**\n\n- 🎯 **Type-safe responses** - Schema validation with any Standard Schema validator\n- 🔗 **Request lifecycle** - Pre/post hooks for authentication and logging\n- 📊 **Built-in telemetry** - Track success rates and response times\n- 🌐 **Universal** - Same API for Node.js and browsers\n- 🪶 **Zero dependencies** - Just the fetch API and smart patterns\n\n### Install\n\n```sh\npnpm add @smooai/fetch\n```\n\n## The Power of Resilient Fetching\n\n### Never Let a Hiccup Break Your App\n\nWatch how @smooai/fetch handles common failure scenarios:\n\n```typescript\nimport fetch from '@smooai/fetch';\n\n// This won't crash if the API is temporarily down\nconst response = await fetch('https://flaky-api.com/data');\n\n// Behind the scenes:\n// Attempt 1: 500 error - waits 500ms\n// Attempt 2: 503 error - waits 1000ms\n// Attempt 3: 200 success! ✅\n```\n\nYour users never know the API had issues - the request just works.\n\n### Respect Rate Limits Automatically\n\nNo more manual retry-after parsing:\n\n```typescript\nconst response = await fetch('https://api.github.com/user/repos');\n\n// If GitHub says \"slow down\":\n// - Sees 429 status + Retry-After: 60\n// - Automatically waits 60 seconds\n// - Retries and succeeds\n// - Your code continues normally\n```\n\n### Production-Ready Examples\n\n#### Node.js Usage\n\n```typescript\nimport fetch from '@smooai/fetch';\n\n// It's just fetch, but resilient\nconst response = await fetch('https://api.example.com/users');\nconst users = await response.json();\n```\n\n#### Browser Usage\n\n```typescript\nimport fetch from '@smooai/fetch/browser';\n\n// Same API, different entry point\nconst response = await fetch('/api/checkout', {\n    method: 'POST',\n    body: { items: cart },\n});\n```\n\n#### Schema Validation That Makes Sense\n\n```typescript\nimport { z } from 'zod';\n\nconst UserSchema = z.object({\n    id: z.string(),\n    email: z.string().email(),\n});\n\n// Your API returns garbage? You'll know immediately\nconst response = await fetch('https://api.example.com/user', {\n    options: { schema: UserSchema },\n});\n\n// response.data is fully typed as { id: string; email: string }\n// No more runtime surprises in production\n```\n\n#### Circuit Breaking for Critical Services\n\n```typescript\nimport { FetchBuilder } from '@smooai/fetch';\n\n// Stop hammering services that are clearly struggling\nconst criticalAPI = new FetchBuilder()\n    .withCircuitBreaker({\n        failureThreshold: 5, // 5 failures\n        failureWindow: 60000, // in 60 seconds\n        recoveryTime: 30000, // try again after 30s\n    })\n    .build();\n\n// If the service is down, this fails fast instead of waiting\ntry {\n    await criticalAPI('https://payment-processor.com/charge');\n} catch (error) {\n    // Circuit is open - service is down\n    // Show fallback UI immediately\n}\n```\n\n## Real-World Scenarios\n\n### Handle Authentication Globally\n\n```typescript\nconst api = new FetchBuilder()\n    .withHooks({\n        preRequest: (url, init) =\u003e {\n            // Add auth header to every request\n            init.headers = {\n                ...init.headers,\n                Authorization: `Bearer ${getToken()}`,\n            };\n            return [url, init];\n        },\n        postResponseError: (url, init, error) =\u003e {\n            if (error.response?.status === 401) {\n                // Token expired - refresh and retry\n                refreshToken();\n            }\n            return error;\n        },\n    })\n    .build();\n```\n\n### Track Performance Automatically\n\n```typescript\nconst api = new FetchBuilder()\n    .withHooks({\n        postResponseSuccess: (url, init, response) =\u003e {\n            // Send metrics to your monitoring service\n            metrics.record({\n                endpoint: url.pathname,\n                duration: response.headers.get('x-response-time'),\n                status: response.status,\n            });\n            return response;\n        },\n    })\n    .build();\n```\n\n### Graceful Degradation\n\n```typescript\n// Primary API with circuit breaker\nconst primaryAPI = new FetchBuilder().withCircuitBreaker({ failureThreshold: 3 }).build();\n\n// Fallback API for resilience\nconst fallbackAPI = new FetchBuilder()\n    .withTimeout(2000) // Faster timeout for fallback\n    .build();\n\nasync function getWeather(city: string) {\n    try {\n        return await primaryAPI(`https://api1.weather.com/${city}`);\n    } catch (error) {\n        // Seamlessly fall back to secondary service\n        console.warn('Primary weather API failed, using fallback');\n        return await fallbackAPI(`https://api2.weather.com/${city}`);\n    }\n}\n```\n\n## The Smart Defaults\n\nOut of the box, @smooai/fetch is configured for the real world:\n\n**Retry Strategy:**\n\n- 2 automatic retries on failure\n- Exponential backoff: 500ms → 1s → 2s\n- Jitter to prevent thundering herds\n- Only retries on network errors or 5xx responses\n\n**Timeout Protection:**\n\n- 10-second default timeout\n- Prevents indefinite hangs\n- Configurable per request\n\n**Rate Limit Handling:**\n\n- Respects Retry-After headers\n- Automatic backoff on 429 responses\n- Prevents API ban hammers\n\n## Seamless Integration with @smooai/logger\n\n@smooai/fetch works perfectly with [@smooai/logger](https://github.com/SmooAI/logger) to provide complete observability across your distributed systems:\n\n### Automatic Correlation ID Propagation\n\n```typescript\nimport fetch from '@smooai/fetch';\nimport { AwsServerLogger } from '@smooai/logger/AwsServerLogger';\n\nconst logger = new AwsServerLogger({ name: 'APIClient' });\n\n// Correlation IDs flow automatically through your requests\nconst api = new FetchBuilder()\n    .withLogger(logger) // That's it!\n    .build();\n\n// In Service A\nlogger.info('Starting user flow'); // Correlation ID: abc-123\nconst user = await api('/users/123'); // Correlation ID sent as header\n\n// In Service B (receiving the request)\n// The correlation ID is automatically extracted and logs are linked!\n```\n\n### Track Every Request with Context\n\n```typescript\nconst api = new FetchBuilder()\n    .withLogger(logger)\n    .withHooks({\n        postResponseSuccess: (url, init, response) =\u003e {\n            // Logger automatically captures:\n            // - Correlation ID\n            // - Request method \u0026 URL\n            // - Response status\n            // - Duration\n            // - Any errors with full context\n            logger.info('API request completed', {\n                endpoint: url.pathname,\n                status: response.status,\n            });\n            return response;\n        },\n    })\n    .build();\n```\n\n### Debug Production Issues Faster\n\nWhen something goes wrong, you'll have the complete story:\n\n```typescript\ntry {\n    const response = await api('/flaky-endpoint');\n} catch (error) {\n    // Logger captures the entire request lifecycle:\n    // - Initial request with headers\n    // - Each retry attempt\n    // - Circuit breaker state changes\n    // - Final error with full stack trace\n    logger.error('Request failed after retries', error);\n}\n\n// In your logs:\n// {\n//   \"correlationId\": \"abc-123\",\n//   \"message\": \"Request failed after retries\",\n//   \"error\": {\n//     \"attempts\": 3,\n//     \"lastError\": \"TimeoutError\",\n//     \"circuitState\": \"open\"\n//   },\n//   \"callerContext\": {\n//     \"stack\": [\"/src/services/UserService.ts:42:16\"]\n//   }\n// }\n```\n\n### Examples\n\n- [Basic Usage](#basic-usage)\n- [FetchBuilder Pattern](#fetchbuilder-pattern)\n- [Retry Example](#retry-example)\n- [Timeout Example](#timeout-example)\n- [Rate Limit Example](#rate-limit-example)\n- [Circuit Breaker Example](#circuit-breaker-example)\n- [Schema Validation Example](#schema-validation-example)\n- [Predefined Authentication Example](#predefined-authentication-example)\n- [Custom Logger Example](#custom-logger-example)\n- [Error Handling](#error-handling)\n\n#### Basic Usage \u003ca name=\"basic-usage\"\u003e\u003c/a\u003e\n\n```typescript\nimport fetch from '@smooai/fetch';\n\n// Simple GET request\nconst response = await fetch('https://api.example.com/data');\n\n// POST request with JSON body and options\nconst response = await fetch('https://api.example.com/data', {\n    method: 'POST',\n    headers: {\n        'Content-Type': 'application/json',\n    },\n    body: {\n        key: 'value',\n    },\n    options: {\n        timeout: {\n            timeoutMs: 5000,\n        },\n        retry: {\n            attempts: 3,\n        },\n    },\n});\n```\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#examples\"\u003eback to examples\u003c/a\u003e)\u003c/p\u003e\n\n#### FetchBuilder Pattern\n\nThe `FetchBuilder` provides a fluent interface for configuring fetch instances:\n\n```typescript\nimport { FetchBuilder, RetryMode } from '@smooai/fetch';\nimport { z } from 'zod';\n\n// Define a response schema\nconst UserSchema = z.object({\n    id: z.string(),\n    name: z.string(),\n    email: z.string().email(),\n});\n\n// Create a configured fetch instance\nconst fetch = new FetchBuilder(UserSchema)\n    .withTimeout(5000) // 5 second timeout\n    .withRetry({\n        attempts: 3,\n        initialIntervalMs: 1000,\n        mode: RetryMode.JITTER,\n    })\n    .withRateLimit(100, 60000) // 100 requests per minute\n    .build();\n\n// Use the configured fetch instance\nconst response = await fetch('https://api.example.com/users/123');\n// response.data is now typed as { id: string; name: string; email: string }\n```\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#examples\"\u003eback to examples\u003c/a\u003e)\u003c/p\u003e\n\n#### Retry Example \u003ca name=\"retry-example\"\u003e\u003c/a\u003e\n\n```typescript\nimport { FetchBuilder, RetryMode } from '@smooai/fetch';\n\n// Using the default fetch\nconst response = await fetch('https://api.example.com/data', {\n    options: {\n        retry: {\n            attempts: 3,\n            initialIntervalMs: 1000,\n            mode: RetryMode.JITTER,\n            factor: 2,\n            jitterAdjustment: 0.5,\n            onRejection: (error) =\u003e {\n                // Custom retry logic\n                if (error instanceof HTTPResponseError) {\n                    return error.response.status \u003e= 500;\n                }\n                return false;\n            },\n        },\n    },\n});\n\n// Or using FetchBuilder\nconst fetch = new FetchBuilder()\n    .withRetry({\n        attempts: 3,\n        initialIntervalMs: 1000,\n        mode: RetryMode.JITTER,\n        factor: 2,\n        jitterAdjustment: 0.5,\n        onRejection: (error) =\u003e {\n            if (error instanceof HTTPResponseError) {\n                return error.response.status \u003e= 500;\n            }\n            return false;\n        },\n    })\n    .build();\n```\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#examples\"\u003eback to examples\u003c/a\u003e)\u003c/p\u003e\n\n#### Timeout Example \u003ca name=\"timeout-example\"\u003e\u003c/a\u003e\n\n```typescript\nimport { FetchBuilder } from '@smooai/fetch';\n\n// Using the default fetch\nconst response = await fetch('https://api.example.com/slow-endpoint', {\n    options: {\n        timeout: {\n            timeoutMs: 5000,\n        },\n    },\n});\n\n// Or using FetchBuilder\nconst fetch = new FetchBuilder()\n    .withTimeout(5000) // 5 second timeout\n    .build();\n\ntry {\n    const response = await fetch('https://api.example.com/slow-endpoint');\n} catch (error) {\n    if (error instanceof TimeoutError) {\n        console.error('Request timed out');\n    }\n}\n```\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#examples\"\u003eback to examples\u003c/a\u003e)\u003c/p\u003e\n\n#### Rate Limit Example \u003ca name=\"rate-limit-example\"\u003e\u003c/a\u003e\n\n```typescript\nimport { FetchBuilder } from '@smooai/fetch';\n\n// Using the default fetch\nconst response = await fetch('https://api.example.com/data', {\n    options: {\n        retry: {\n            attempts: 1,\n            initialIntervalMs: 1000,\n            onRejection: (error) =\u003e {\n                if (error instanceof RatelimitError) {\n                    return error.remainingTimeInRatelimit;\n                }\n                return false;\n            },\n        },\n    },\n});\n\n// Or using FetchBuilder\nconst fetch = new FetchBuilder()\n    .withRateLimit(100, 60000, {\n        attempts: 1,\n        initialIntervalMs: 1000,\n        onRejection: (error) =\u003e {\n            if (error instanceof RatelimitError) {\n                return error.remainingTimeInRatelimit;\n            }\n            return false;\n        },\n    })\n    .build();\n```\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#examples\"\u003eback to examples\u003c/a\u003e)\u003c/p\u003e\n\n#### Schema Validation Example\n\n```typescript\nimport { FetchBuilder } from '@smooai/fetch';\nimport { z } from 'zod';\n\n// Define response schema\nconst UserSchema = z.object({\n    id: z.string(),\n    name: z.string(),\n    email: z.string().email(),\n});\n\n// Using the default fetch\nconst response = await fetch('https://api.example.com/users/123', {\n    options: {\n        schema: UserSchema,\n    },\n});\n\n// Or using FetchBuilder\nconst fetch = new FetchBuilder(UserSchema).build();\n\ntry {\n    const response = await fetch('https://api.example.com/users/123');\n    // response.data is typed as { id: string; name: string; email: string }\n} catch (error) {\n    if (error instanceof HumanReadableSchemaError) {\n        console.error('Validation failed:', error.message);\n        // Example output:\n        // Validation failed: Invalid email format at path: email\n    }\n}\n```\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#examples\"\u003eback to examples\u003c/a\u003e)\u003c/p\u003e\n\n#### Lifecycle Hooks Example\n\n```typescript\nimport { FetchBuilder } from '@smooai/fetch';\nimport { z } from 'zod';\n\n// Define response schema\nconst UserSchema = z.object({\n    id: z.string(),\n    name: z.string(),\n    email: z.string().email(),\n});\n\n// Create a fetch instance with hooks\nconst fetch = new FetchBuilder(UserSchema)\n    .withHooks({\n        // Pre-request hook can modify both URL and request configuration\n        preRequest: (url, init) =\u003e {\n            // Add timestamp to URL\n            const modifiedUrl = new URL(url.toString());\n            modifiedUrl.searchParams.set('timestamp', Date.now().toString());\n\n            // Add custom headers\n            init.headers = {\n                ...init.headers,\n                'X-Custom-Header': 'value',\n            };\n\n            return [modifiedUrl, init];\n        },\n\n        // Post-response success hook can modify the response\n        // Note: url and init are readonly in this hook\n        postResponseSuccess: (url, init, response) =\u003e {\n            if (response.isJson \u0026\u0026 response.data) {\n                // Add request metadata to response\n                response.data = {\n                    ...response.data,\n                    _metadata: {\n                        requestUrl: url.toString(),\n                        requestMethod: init.method,\n                        processedAt: new Date().toISOString(),\n                    },\n                };\n            }\n            return response;\n        },\n\n        // Post-response error hook can handle or transform errors\n        // Note: url and init are readonly in this hook\n        postResponseError: (url, init, error, response) =\u003e {\n            if (error instanceof HTTPResponseError) {\n                // Create a more detailed error message\n                return new Error(`Request to ${url} failed with status ${error.response.status}. ` + `Method: ${init.method}`);\n            }\n            return error;\n        },\n    })\n    .build();\n\n// Use the configured fetch instance\ntry {\n    const response = await fetch('https://api.example.com/users/123');\n    // response.data includes the _metadata added by postResponseSuccess\n    console.log(response.data);\n} catch (error) {\n    // Error message includes details added by postResponseError\n    console.error(error.message);\n}\n```\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#examples\"\u003eback to examples\u003c/a\u003e)\u003c/p\u003e\n\n#### Predefined Authentication Example\n\n```typescript\nimport { FetchBuilder } from '@smooai/fetch';\nimport { z } from 'zod';\n\n// Define response schema\nconst UserSchema = z.object({\n    id: z.string(),\n    name: z.string(),\n    email: z.string().email(),\n});\n\n// Using the default fetch\nconst response = await fetch('https://api.example.com/users/123', {\n    headers: {\n        Authorization: 'Bearer your-auth-token',\n        'X-API-Key': 'your-api-key',\n        'X-Client-ID': 'your-client-id',\n    },\n    options: {\n        schema: UserSchema,\n    },\n});\n\n// Or using FetchBuilder\nconst fetch = new FetchBuilder(UserSchema)\n    .withInit({\n        headers: {\n            Authorization: 'Bearer your-auth-token',\n            'X-API-Key': 'your-api-key',\n            'X-Client-ID': 'your-client-id',\n        },\n    })\n    .build();\n\n// All requests will automatically include the auth headers\nconst response = await fetch('https://api.example.com/users/123');\n```\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#examples\"\u003eback to examples\u003c/a\u003e)\u003c/p\u003e\n\n#### Custom Logger Example\n\n```typescript\nimport { FetchBuilder } from '@smooai/fetch';\nimport { AwsServerLogger } from '@smooai/logger/AwsServerLogger';\nimport { z } from 'zod';\n\n// Use @smooai/logger for automatic context and correlation\nconst logger = new AwsServerLogger({\n    name: 'MyAPI',\n    prettyPrint: true, // Human-readable logs in development\n});\n\n// Create a fetch instance with the logger\nconst fetch = new FetchBuilder(\n    z.object({\n        id: z.string(),\n        name: z.string(),\n    }),\n)\n    .withLogger(logger)\n    .build();\n\n// All requests now include:\n// - Correlation IDs that flow across services\n// - Automatic performance tracking\n// - Full error context with stack traces\n// - Request/response details\nconst response = await fetch('https://api.example.com/users/123');\n\n// Or bring your own logger that implements LoggerInterface\nconst customLogger = {\n    debug: (message: string, ...args: any[]) =\u003e {\n        /* ... */\n    },\n    info: (message: string, ...args: any[]) =\u003e {\n        /* ... */\n    },\n    warn: (message: string, ...args: any[]) =\u003e {\n        /* ... */\n    },\n    error: (error: Error | unknown, message: string, ...args: any[]) =\u003e {\n        /* ... */\n    },\n};\n```\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#examples\"\u003eback to examples\u003c/a\u003e)\u003c/p\u003e\n\n#### Error Handling \u003ca name=\"error-handling\"\u003e\u003c/a\u003e\n\n```typescript\nimport fetch, { HTTPResponseError, RatelimitError, RetryError, TimeoutError } from '@smooai/fetch';\n\ntry {\n    const response = await fetch('https://api.example.com/data');\n} catch (error) {\n    if (error instanceof HTTPResponseError) {\n        console.error('HTTP Error:', error.response.status);\n        console.error('Response Data:', error.response.data);\n    } else if (error instanceof RetryError) {\n        console.error('Retry failed after all attempts');\n    } else if (error instanceof TimeoutError) {\n        console.error('Request timed out');\n    } else if (error instanceof RatelimitError) {\n        console.error('Rate limit exceeded');\n    }\n}\n```\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#examples\"\u003eback to examples\u003c/a\u003e)\u003c/p\u003e\n\n### Built With\n\n- TypeScript\n- Native Fetch API\n- [Mollitia](https://github.com/genesys/mollitia) (Circuit Breaker, Rate Limiter)\n- [Standard Schema](https://github.com/standard-schema/standard-schema)\n- [@smooai/logger](https://github.com/SmooAI/logger) for structured logging (bring your own logger supported)\n- [@smooai/utils](https://github.com/SmooAI/utils) for Standard Schema validation and human-readable error generation\n\n## Contributing\n\nContributions are welcome! This project uses [changesets](https://github.com/changesets/changesets) to manage versions and releases.\n\n### Development Workflow\n\n1. Fork the repository\n2. Create your branch (`git checkout -b amazing-feature`)\n3. Make your changes\n4. Add a changeset to document your changes:\n\n    ```sh\n    pnpm changeset\n    ```\n\n    This will prompt you to:\n    - Choose the type of version bump (patch, minor, or major)\n    - Provide a description of the changes\n\n5. Commit your changes (`git commit -m 'Add some amazing feature'`)\n6. Push to the branch (`git push origin feature/amazing-feature`)\n7. Open a Pull Request\n\n### Pull Request Guidelines\n\n- Reference any related issues in your PR description\n\nThe maintainers will review your PR and may request changes before merging.\n\n\u003c!-- CONTACT --\u003e\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#readme-top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n## Contact\n\nBrent Rager\n\n- [Email](mailto:brent@smoo.ai)\n- [LinkedIn](https://www.linkedin.com/in/brentrager/)\n- [BlueSky](https://bsky.app/profile/brentragertech.bsky.social)\n- [TikTok](https://www.tiktok.com/@brentragertech)\n- [Instagram](https://www.instagram.com/brentragertech/)\n\nSmoo Github: [https://github.com/SmooAI](https://github.com/SmooAI)\n\n\u003cp align=\"right\"\u003e(\u003ca href=\"#readme-top\"\u003eback to top\u003c/a\u003e)\u003c/p\u003e\n\n\u003c!-- MARKDOWN LINKS \u0026 IMAGES --\u003e\n\u003c!-- https://www.markdownguide.org/basic-syntax/#reference-style-links --\u003e\n\n[sst.dev-url]: https://reactjs.org/\n[sst]: https://img.shields.io/badge/sst-EDE1DA?style=for-the-badge\u0026logo=sst\u0026logoColor=E27152\n[sst-url]: https://sst.dev/\n[next]: https://img.shields.io/badge/next.js-000000?style=for-the-badge\u0026logo=nextdotjs\u0026logoColor=white\n[next-url]: https://nextjs.org/\n[aws]: https://img.shields.io/badge/aws-232F3E?style=for-the-badge\u0026logo=amazonaws\u0026logoColor=white\n[aws-url]: https://tailwindcss.com/\n[tailwindcss]: https://img.shields.io/badge/tailwind%20css-0B1120?style=for-the-badge\u0026logo=tailwindcss\u0026logoColor=#06B6D4\n[tailwindcss-url]: https://tailwindcss.com/\n[zod]: https://img.shields.io/badge/zod-3E67B1?style=for-the-badge\u0026logoColor=3E67B1\n[zod-url]: https://zod.dev/\n[sanity]: https://img.shields.io/badge/sanity-F36458?style=for-the-badge\n[sanity-url]: https://www.sanity.io/\n[vitest]: https://img.shields.io/badge/vitest-1E1E20?style=for-the-badge\u0026logo=vitest\u0026logoColor=#6E9F18\n[vitest-url]: https://vitest.dev/\n[pnpm]: https://img.shields.io/badge/pnpm-F69220?style=for-the-badge\u0026logo=pnpm\u0026logoColor=white\n[pnpm-url]: https://pnpm.io/\n[turborepo]: https://img.shields.io/badge/turborepo-000000?style=for-the-badge\u0026logo=turborepo\u0026logoColor=#EF4444\n[turborepo-url]: https://turbo.build/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmooai%2Ffetch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsmooai%2Ffetch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmooai%2Ffetch/lists"}