{"_id":"@clement_lores/quick-fetch","name":"@clement_lores/quick-fetch","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@clement_lores/quick-fetch","version":"1.0.0","description":"A modern, fetch-based HTTP client: zero-dependency, typed, minimal, and secure by default.","homepage":"https://github.com/loresclement/quick-fetch#readme","bugs":{"url":"https://github.com/loresclement/quick-fetch/issues"},"repository":{"type":"git","url":"git+https://github.com/loresclement/quick-fetch.git"},"license":"MIT","author":{"name":"Clément LORES"},"type":"module","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"keywords":["fetch","http-client","typescript","zero-dependency","axios-alternative"],"main":"index.js","scripts":{"build":"tsup","typecheck":"tsc --noEmit","test":"npm run build && node --test test/*.test.js","prepublishOnly":"npm run typecheck && npm run build && npm test"},"devDependencies":{"@types/node":"^26.0.1","tsup":"^8.5.1","typescript":"^6.0.3"},"_id":"@clement_lores/quick-fetch@1.0.0","_nodeVersion":"24.13.1","_npmVersion":"11.8.0","dist":{"integrity":"sha512-3EYO4V5wLsQTTWR9/R/F2FQu3EMKggLtwM2Fn/VE+GVx4RWxBQmKefRjw+D1goEjq5KAiky6iA+BCoXotnqyOw==","shasum":"c3133cd31ac1aac3111f664e700c69f5408d03fc","tarball":"https://registry.npmjs.org/@clement_lores/quick-fetch/-/quick-fetch-1.0.0.tgz","fileCount":8,"unpackedSize":57066,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCsGR28T2ZQa3XRtZXI2hvRf1aNqzHM0Gg82kIVsODWsQIhAIuP3lL1iAORWV97K3AOYkG6HDAxJSBaFzfglgPCDUIW"}]},"_npmUser":{"name":"clement_lores","email":"contact@clement-lores.fr"},"directories":{},"maintainers":[{"name":"clement_lores","email":"contact@clement-lores.fr"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/quick-fetch_1.0.0_1782560125547_0.6923153655209604"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-27T11:35:25.344Z","1.0.0":"2026-06-27T11:35:25.697Z","modified":"2026-06-27T11:35:25.963Z"},"maintainers":[{"name":"clement_lores","email":"contact@clement-lores.fr"}],"description":"A modern, fetch-based HTTP client: zero-dependency, typed, minimal, and secure by default.","homepage":"https://github.com/loresclement/quick-fetch#readme","keywords":["fetch","http-client","typescript","zero-dependency","axios-alternative"],"repository":{"type":"git","url":"git+https://github.com/loresclement/quick-fetch.git"},"author":{"name":"Clément LORES"},"bugs":{"url":"https://github.com/loresclement/quick-fetch/issues"},"license":"MIT","readme":"# quick-fetch\n\nquick-fetch is a zero-dependency, TypeScript-first HTTP client built on native fetch.\n\nIt keeps everything you like about fetch, while removing repetitive boilerplate for day-to-day REST API calls.\n\n## Why quick-fetch\n\nNative fetch is powerful, but most API layers still repeat the same plumbing:\n\n- Building URLs and query strings\n- Handling JSON request/response bodies\n- Managing per-request timeouts\n- Throwing structured errors for non-2xx responses\n- Repeating headers and base URL setup\n\nquick-fetch gives you a clean, minimal abstraction without introducing heavy dependencies or a custom transport layer.\n\n## Highlights\n\n- ZERO dependencies\n- TypeScript-first API\n- ESM + CommonJS support\n- Built on native fetch, AbortController, URL, and FormData\n- Automatic response parsing (JSON, text, and 204 handling)\n- Structured HttpError for non-2xx responses\n- Built-in timeout support with TimeoutError\n- Safe guardrail for invalid body options (json + formdata)\n- Base URL, global headers, and per-request override support\n- Logging modes: basic, detailed, full\n\n## Install\n\nnpm:\n\n```bash\nnpm install @clement_lores/quick-fetch\n```\n\n## Runtime Support\n\nquick-fetch works in modern runtimes that provide native web APIs used by fetch-based clients:\n\n- Browsers (modern)\n- Node.js 18+ (recommended)\n- Deno\n- Bun\n- Edge runtimes with fetch-compatible APIs\n\nNo additional dependency is required by quick-fetch itself.\n\n## Quick Start\n\n```ts\nimport { createClient } from \"quick-fetch\";\n\ntype User = {\n\tid: string;\n\tname: string;\n\temail: string;\n};\n\nconst api = createClient({\n\tbaseUrl: \"https://api.example.com\",\n\theaders: {\n\t\tAuthorization: \"Bearer <token>\"\n\t},\n\ttimeout: 8000,\n\tlogging: \"basic\"\n});\n\nconst users = await api.get<User[]>(\"/users\", {\n\tquery: {\n\t\tpage: 1,\n\t\tsearch: \"john\"\n\t}\n});\n```\n\n## API Overview\n\n### createClient(options?)\n\nCreates a reusable HTTP client instance.\n\n```ts\nimport { createClient } from \"quick-fetch\";\n\nconst api = createClient({\n\tbaseUrl: \"https://api.example.com\",\n\theaders: { \"x-app\": \"my-service\" },\n\ttimeout: 5000,\n\tlogging: \"detailed\"\n});\n```\n\n#### ClientOptions\n\n```ts\ntype ClientOptions = {\n\tbaseUrl?: string;\n\theaders?: HeadersInit;\n\ttimeout?: number;\n\tlogging?: \"basic\" | \"detailed\" | \"full\";\n};\n```\n\nOption details:\n\n- baseUrl: Base URL used with relative request paths. Strongly recommended in production.\n- headers: Default headers applied to every request.\n- timeout: Default timeout in milliseconds for all requests.\n- logging:\n- basic: Logs request method/path and body type.\n- detailed: Logs request details and body payload overview.\n- full: Adds response status and duration.\n\n### Client Methods\n\nAll methods are generic and return Promise<TResponse>.\n\n```ts\napi.get<TResponse>(path, options?)\napi.post<TResponse>(path, options?)\napi.put<TResponse>(path, options?)\napi.delete<TResponse>(path, options?)\napi.patch<TResponse>(path, options?)\napi.head<TResponse>(path, options?)\napi.options<TResponse>(path, options?)\n```\n\n### RequestOptions\n\n```ts\ntype RequestOptions = {\n\theaders?: HeadersInit;\n\tquery?: Record<string, QueryParamValue | QueryParamValue[]>;\n\tjson?: unknown;\n\tformdata?: FormData;\n\ttimeout?: number;\n\tsignal?: AbortSignal;\n};\n\ntype QueryParamValue = string | number | boolean | null | undefined;\n```\n\nRule:\n\n- Use either json or formdata, never both in one request.\n\nIf both are provided, quick-fetch throws MalformedParamsError.\n\n## REST Examples (TypeScript)\n\n### GET with Query Parameters\n\n```ts\ntype ProductsResponse = {\n\titems: Array<{ id: string; name: string }>;\n\ttotal: number;\n};\n\nconst products = await api.get<ProductsResponse>(\"/products\", {\n\tquery: {\n\t\tpage: 1,\n\t\tsearch: \"helmet\",\n\t\tavailable: true,\n\t\tbrand: [\"Honda\", \"Yamaha\"],\n\t\tunused: undefined,\n\t\tnullable: null\n\t}\n});\n```\n\nBehavior:\n\n- Arrays are expanded as repeated query keys.\n- null and undefined values are skipped.\n\n### POST JSON Body\n\n```ts\ntype CreateUserPayload = {\n\tname: string;\n\temail: string;\n};\n\ntype CreateUserResponse = {\n\tid: string;\n\tname: string;\n\temail: string;\n};\n\nconst created = await api.post<CreateUserResponse>(\"/users\", {\n\tjson: {\n\t\tname: \"Ada Lovelace\",\n\t\temail: \"ada@example.com\"\n\t} satisfies CreateUserPayload\n});\n```\n\nWhen json is provided:\n\n- Request body is JSON.stringify(json).\n- content-type: application/json is set automatically.\n\n### POST FormData (File Upload)\n\n```ts\nconst form = new FormData();\nform.append(\"avatar\", fileInput.files?.[0] as File);\nform.append(\"displayName\", \"Ada\");\n\nconst result = await api.post<{ ok: boolean }>(\"/profile/avatar\", {\n\tformdata: form\n});\n```\n\nWhen formdata is provided:\n\n- Body is sent as multipart/form-data.\n- content-type boundary is handled by runtime automatically.\n\n### Per-request Timeout Override\n\n```ts\nconst slowReport = await api.get<{ status: string }>(\"/reports/daily\", {\n\ttimeout: 15000\n});\n```\n\n### Manual Cancellation\n\n```ts\nconst controller = new AbortController();\n\nconst pending = api.get<{ ok: true }>(\"/long-task\", {\n\tsignal: controller.signal\n});\n\ncontroller.abort();\n\nawait pending;\n```\n\n## Error Handling\n\nquick-fetch throws explicit, typed errors for common API failure modes.\n\n### Error Types\n\n#### HttpError\n\nThrown for all non-2xx responses.\n\nProperties:\n\n- name: HttpError\n- status: number\n- statusText: string\n- url: string\n- method: HTTP method\n- body: Parsed response body (JSON or text)\n\n#### TimeoutError\n\nThrown when request timeout is reached.\n\n#### MalformedParamsError\n\nThrown when both json and formdata are provided in the same request.\n\n### Handling Errors Safely\n\n```ts\nimport { HttpError, TimeoutError, MalformedParamsError } from \"quick-fetch\";\n\ntry {\n\tconst data = await api.get<{ id: string }>(\"/users/unknown\");\n\tconsole.log(data);\n} catch (error) {\n\tif (error instanceof HttpError) {\n\t\tconsole.error(\"HTTP failure\", {\n\t\t\tstatus: error.status,\n\t\t\tstatusText: error.statusText,\n\t\t\tmethod: error.method,\n\t\t\turl: error.url,\n\t\t\tbody: error.body\n\t\t});\n\t} else if (error instanceof TimeoutError) {\n\t\tconsole.error(\"Request timeout\", error.message);\n\t} else if (error instanceof MalformedParamsError) {\n\t\tconsole.error(\"Request options are invalid\", error.message);\n\t} else {\n\t\tconsole.error(\"Unexpected error\", error);\n\t}\n}\n```\n\nImportant behavior note:\n\n- Timeout-triggered aborts become TimeoutError.\n- Manual AbortSignal cancellation uses the native AbortError from fetch/runtime.\n\n## Response Parsing\n\nquick-fetch parses response bodies automatically:\n\n- Status 204: returns undefined\n- content-type includes application/json: returns parsed JSON\n- Otherwise: returns text\n\nThis applies to both successful and error responses (HttpError.body is parsed too).\n\n## Logging Modes\n\nSet logging in createClient:\n\n```ts\nconst api = createClient({\n\tbaseUrl: \"https://api.example.com\",\n\tlogging: \"full\"\n});\n```\n\nModes:\n\n- basic: Logs request method/path and body type indicator.\n- detailed: Adds request payload details for easier debugging.\n- full: Includes detailed request data plus colored response status and duration.\n\n## Header Strategy\n\nquick-fetch merges headers like this:\n\n- Start from client-level headers\n- Apply request-level headers on top\n- Request-level values override duplicates\n\nExample:\n\n```ts\nconst api = createClient({\n\tbaseUrl: \"https://api.example.com\",\n\theaders: {\n\t\tAuthorization: \"Bearer token-1\",\n\t\t\"x-app\": \"dashboard\"\n\t}\n});\n\nawait api.get(\"/users\", {\n\theaders: {\n\t\tAuthorization: \"Bearer token-2\"\n\t}\n});\n```\n\n## Best Practices\n\n- Always provide baseUrl for production clients.\n- Type every response with method generics.\n- Centralize HTTP error handling around HttpError.\n- Use client-level headers for stable defaults.\n- Use request-level timeout for known slow endpoints.\n- Keep logging to basic or detailed in production unless actively debugging.\n\n## Comparison\n\n### quick-fetch vs native fetch\n\n- quick-fetch adds typed method wrappers for REST calls.\n- quick-fetch gives structured HttpError for non-2xx responses.\n- quick-fetch auto-parses JSON/text and handles 204 gracefully.\n- quick-fetch supports built-in timeout handling and query object serialization.\n- native fetch remains the underlying transport layer.\n\n### quick-fetch vs axios\n\n- quick-fetch has zero dependencies.\n- quick-fetch stays close to native fetch standards.\n- quick-fetch is intentionally minimal and focused on REST request ergonomics.\n- axios provides a broader feature set (interceptors, richer transforms, etc.).\n\n## Current Scope and Limitations\n\nquick-fetch intentionally stays small and focused.\n\nNot included by design in current stable scope:\n\n- Automatic retries\n- Interceptors/middleware pipeline\n- Built-in request/response schema validation\n- Built-in caching strategy\n- GraphQL-specific helpers\n\n## Stability and Versioning\n\n- API stability target: stable\n- Versioning strategy: semantic versioning (SemVer)\n\n## FAQ\n\n### Does quick-fetch replace fetch?\n\nNo. It builds on top of native fetch and keeps fetch semantics.\n\n### Can I use absolute URLs without baseUrl?\n\nYes. baseUrl is optional. However, using baseUrl is recommended for consistency and maintainability.\n\n### Does quick-fetch parse error responses?\n\nYes. Non-2xx responses throw HttpError with a parsed body field when possible.\n\n### What happens with HEAD requests?\n\nA 204 response returns undefined. For non-JSON textual responses, text is returned.\n\n### Is this package browser-only?\n\nNo. It is runtime-agnostic as long as native fetch-compatible APIs are available.\n\n## License\n\nMIT\n\nCopyright (c) 2026 Clément LORES\n\n## Author\n\nGitHub: https://github.com/loresclement\n\n## Repository\n\nhttps://github.com/loresclement/quick-fetch","readmeFilename":"README.md","_rev":"1-ad56eee5334b06122d7460255f7364ca"}