{"id":28540134,"url":"https://github.com/zellwk/zl-fetch","last_synced_at":"2025-07-07T10:32:03.954Z","repository":{"id":19327381,"uuid":"86794149","full_name":"zellwk/zl-fetch","owner":"zellwk","description":"A library that makes the Fetch API a breeze","archived":false,"fork":false,"pushed_at":"2025-06-11T21:01:06.000Z","size":2684,"stargazers_count":294,"open_issues_count":1,"forks_count":33,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-07-06T04:47:27.504Z","etag":null,"topics":["ajax","browser","fetch","fetch-api","javascript","nodejs"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/zellwk.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2017-03-31T08:05:04.000Z","updated_at":"2025-06-30T10:21:38.000Z","dependencies_parsed_at":"2024-06-18T18:34:07.661Z","dependency_job_id":"c05aaf13-3312-464a-8ce4-f7ca81aa426a","html_url":"https://github.com/zellwk/zl-fetch","commit_stats":{"total_commits":166,"total_committers":4,"mean_commits":41.5,"dds":"0.048192771084337394","last_synced_commit":"61c3af6dac60554b52498529778459f16e881203"},"previous_names":[],"tags_count":52,"template":false,"template_full_name":null,"purl":"pkg:github/zellwk/zl-fetch","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zellwk%2Fzl-fetch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zellwk%2Fzl-fetch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zellwk%2Fzl-fetch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zellwk%2Fzl-fetch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zellwk","download_url":"https://codeload.github.com/zellwk/zl-fetch/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zellwk%2Fzl-fetch/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263851349,"owners_count":23519815,"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":["ajax","browser","fetch","fetch-api","javascript","nodejs"],"created_at":"2025-06-09T19:08:25.346Z","updated_at":"2025-07-07T10:32:03.941Z","avatar_url":"https://github.com/zellwk.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# zlFetch\n\nzlFetch is a wrapper around fetch that provides you with a convenient way to make requests.\n\nIt's features are as follows:\n\n- Quality of life improvements over the native `fetch` function\n\n  - [Use the response right away](#quick-start) without using `response.json()`, `response.text()`, or `response.blob()`.\n  - [Promise-like error handling](#error-handling) — all 400 and 500 errors are directed into the `catch` block automatically.\n  - [Easy error handling when using `await`](#easy-error-handling-when-using-asyncawait) — errors can be returned so you don't have to write a `try/catch` block.\n  - [Built-in abort functionality](#aborting-the-request)\n  - Streaming capabilitiies with [pure Fetch](#streaming-with-fetch) and [Event Source](#streaming-with-event-source)\n\n- Additional improvements over the native `fetch` function\n  - `Content-Type` headers are set [automatically](#content-type-generation-based-on-body-content) based on the `body` content.\n  - [Get everything you need](#the-response-contains-all-the-data-you-may-need) about your response — `headers`, `body`, `status`, and more.\n  - [Debug your request](#debugging-the-request) without looking at the Network panel\n  - Shorthand for `GET`, `POST`, `PUT`, `PATCH`, and `DELETE` methods\n  - [Helper for generating query strings](#query-string-helpers) so you don't have to mess with query parameters.\n  - [Generates authorization headers](#authorization-header-helpers) with an `auth` property.\n  - [Create instances to hold url and options](#creating-a-zlfetch-instance) so you don't have to repeat yourself.\n\nNote: zlFetch is a ESM library since `v4.0.0`.\n\n# Table of Contents\n\n- [zlFetch](#zlfetch)\n- [Table of Contents](#table-of-contents)\n  - [Installing zlFetch](#installing-zlfetch)\n    - [Through npm (recommended)](#through-npm-recommended)\n    - [Through browsers](#through-browsers)\n  - [Quick Start](#quick-start)\n    - [Shorthand methods for GET, POST, PUT, PATCH, and DELETE](#shorthand-methods-for-get-post-put-patch-and-delete)\n    - [Supported response types](#supported-response-types)\n    - [The response contains all the data you may need](#the-response-contains-all-the-data-you-may-need)\n    - [Debugging the request](#debugging-the-request)\n    - [Error Handling](#error-handling)\n    - [Easy error handling when using `async`/`await`](#easy-error-handling-when-using-asyncawait)\n  - [Streaming with Fetch](#streaming-with-fetch)\n    - [Server-Sent Events (SSE)](#server-sent-events-sse)\n    - [`Transfer-Encoding: chunked`](#transfer-encoding-chunked)\n    - [Other Streams](#other-streams)\n  - [Aborting the request](#aborting-the-request)\n    - [Basic Usage](#basic-usage)\n    - [With async/await](#with-asyncawait)\n    - [Passing in abort controller manually](#passing-in-abort-controller-manually)\n  - [Helpful Features](#helpful-features)\n    - [Query string helpers](#query-string-helpers)\n    - [`Content-Type` generation based on `body` content](#content-type-generation-based-on-body-content)\n    - [Authorization header helpers](#authorization-header-helpers)\n    - [Creating a zlFetch Instance](#creating-a-zlfetch-instance)\n    - [Custom response handler](#custom-response-handler)\n  - [Streaming with event source](#streaming-with-event-source)\n    - [Listen to any event](#listen-to-any-event)\n    - [Using the Fetch Version](#using-the-fetch-version)\n      - [Setting Retry Interval](#setting-retry-interval)\n    - [Closing an event source](#closing-an-event-source)\n\n## Installing zlFetch\n\n### Through npm (recommended)\n\n```bash\n# Installing through npm\nnpm install zl-fetch --save\n```\n\nThen you can use it by importing it in your JavaScript file.\n\n```js\nimport zlFetch from 'zl-fetch'\n```\n\n### Through browsers\n\nYou can import zlFetch directly into JavaScript through a CDN.\n\nTo do this, you first need to set your `script`'s type to `module`, then import `zlFetch`.\n\n```html\n\u003cscript type=\"module\"\u003e\n  import zlFetch from 'https://cdn.jsdelivr.net/npm/zl-fetch@6.0.0/src/index.js'\n\u003c/script\u003e\n```\n\n## Quick Start\n\nYou can use zlFetch just like a normal `fetch` function. The only difference is you don't have to write a `response.json` or `response.text` method anymore!\n\nzlFetch handles it for you automatically so you can go straight to using your response.\n\n```js\nzlFetch('url')\n  .then(response =\u003e console.log(response))\n  .catch(error =\u003e console.log(error))\n```\n\n### Shorthand methods for GET, POST, PUT, PATCH, and DELETE\n\nzlFetch contains shorthand methods for these common REST methods so you can use them quickly.\n\n```js\nzlFetch.get(/* some-url */)\nzlFetch.post(/* some-url */)\nzlFetch.put(/* some-url */)\nzlFetch.patch(/* some-url */)\nzlFetch.delete(/* some-url */)\n```\n\n### Supported response types\n\nzlFetch supports `json`, `text`, and `blob` response types so you don't have to write `response.json()`, `response.text()` or `response.blob()`.\n\nIt also supports streams. See [streaming](##streaming) for a better understanding of how this works.\n\nOther response types are not supported right now. If you need to support other response types, consider using your own [response handler](#custom-response-handler)\n\n### The response contains all the data you may need\n\nzlFetch sends you all the data you need in the `response` object. This includes the following:\n\n- `headers`: response headers\n- `body`: response body\n- `status`: response status\n- `statusText`: response status text\n- `response`: original response from Fetch\n\nWe do this so you don't have to fish out the `headers`, `status`, `statusText` or even the rest of the `response` object by yourself.\n\n### Debugging the request\n\nNew in `v4.0.0`: You can debug the request object by adding a `debug` option. This will reveal a `debug` object that contains the request being constructed.\n\n- `url`\n- `method`\n- `headers`\n- `body`\n\n```js\nzlFetch('url', { debug: true })\n  .then({ debug } =\u003e console.log(debug))\n```\n\n### Error Handling\n\nzlFetch directs all 400 and 500 errors to the `catch` method. Errors contain the same information as a response.\n\n- `headers`: response headers\n- `body`: response body\n- `status`: response status\n- `statusText`: response status text\n- `response`: original response from fetch\n\nThis makes is zlFetch super easy to use with promises.\n\n```js\nzlFetch('some-url').catch(error =\u003e {\n  /* Handle error */\n})\n\n// The above request can be written in Fetch like this:\nfetch('some-url')\n  .then(response =\u003e {\n    if (!response.ok) {\n      Promise.reject(response.json)\n    }\n  })\n  .catch(error =\u003e {\n    /* Handle error */\n  })\n```\n\n### Easy error handling when using `async`/`await`\n\nzlFetch lets you pass all errors into an `errors` object. You can do this by adding a `returnError` option. This is useful when you work a lot with `async/await`.\n\n```js\nconst { response, error } = await zlFetch('some-url', {\n  returnError: true,\n})\n```\n\n## Streaming with Fetch\n\nzlFetch supports streaming in `v6.2.0`. It detects streams when you pass in `stream: true` as an option. It also provides a readable stream helper to decode the stream.\n\nThe following can be detected as streams:\n\n- `Content-Type` header is `text/event-stream`\n- Header contains `Transfer-Encoding: chunked`\n- There is no `Content-Length` header\n\nThe decoded stream is stored inside `response.body` so you can simply loop through it to get your chunks. Below are a few caveats you need to know because the implementation changes _slightly_ due depending on how servers implement streaming.\n\n### Server-Sent Events (SSE)\n\nzlFetch decodes the request body for you automatically for server-sent events. Just loop through the `request.body` to get your chunks.\n\n```js\nconst response = await zlFetch('/sse-endpoint', { stream: true })\n\nfor await (const chunk of response.body) {\n  // Do something with chunk\n  chunks.push(chunk)\n}\n```\n\nThe pure `zlFetch` function might not be the best at handling SSE because it doesn't reconnect automatically when the connection is lost. See [Streaming with Event Source](#streaming-with-event-source) for a recommended approach.\n\n### `Transfer-Encoding: chunked`\n\nThe `Transfer-Encoding` header does not reach the browser the stream will not be decoded automatically. To decode it in the browser, use the `readStream` helper we provided. No need for this extra step if you're sending the Fetch request from a server.\n\n```js\nimport { readStream } from 'zl-fetch'\n\nconst response = await zlFetch('/sse-endpoint', { stream: true })\n\n// For Browsers\nconst stream = readStream(response.body)\nfor await (const chunk of stream) {\n  /* ...*/\n}\n\n// For Servers\nfor await (const chunk of response.body) {\n  /* ... */\n}\n```\n\n### Other Streams\n\nzlFetch detects it's a stream if there is no `Content-Length` header. For these streams, the `Transfer-Encoding: chunked` rules above apply.\n\n\u003c!--\n### Progress Tracking\n\nYou can track download progress by monitoring the stream:\n\n```js\nconst response = await zlFetch('large-file')\nconst contentLength = response.headers.get('content-length')\nlet receivedLength = 0\n\nwhile (true) {\n  const { done, value } = await reader.read()\n  if (done) break\n\n  receivedLength += value.length\n  const progress = (receivedLength / contentLength) * 100\n  console.log(`Download progress: ${progress.toFixed(2)}%`)\n}\n``` --\u003e\n\n## Aborting the request\n\nYou can abort a request — both normal and streams — using the same `abort()` method. This is useful for:\n\n- Canceling long-running requests\n- Stopping requests when a user navigates away\n- Implementing request timeouts\n- Canceling requests when new data is needed\n\n### Basic Usage\n\n```js\n// With promises\nconst request = zlFetch('endpoint')\n\n// Aborts the request\nrequest.abort()\n\n// Handle the abort\nrequest.catch(error =\u003e {\n  if (error.name === 'AbortError') {\n    console.log('Request was aborted')\n  }\n})\n```\n\nWe've added the `abort` function to the `.then` call so you can call it while handling the response.\n\n```js\n// With promises\nconst request = zlFetch('endpoint')\n  .then(response =\u003e {\n    // Aborts the request\n    response.abort()\n  })\n  .catch(error =\u003e {\n    // Handle the abort error\n    if (error.name === 'AbortError') {\n      console.log('Request was aborted')\n    }\n  })\n```\n\n### With async/await\n\nYou can also handle aborts when using async/await:\n\n```js\ntry {\n  const response = await zlFetch('endpoint')\n  // Aborts the request\n  response.abort()\n} catch (error) {\n  if (error.name === 'AbortError') {\n    console.log('Request was aborted')\n  }\n}\n```\n\n### Passing in abort controller manually\n\nIf you wish to, you can pass in your abort controller as well. No need to pass the abort `signal` — we'll create a signal from that controller for the abort method.\n\n```js\nconst customAbortController = new AbortController()\nconst response = await zlFetch('endpoint', {\n  controller: customAbortController,\n})\n\n// Abort the request\nresponse.abort()\n```\n\n## Helpful Features\n\n### Query string helpers\n\nYou can add `query` or `queries` as an option and zlFetch will create a query string for you automatically. Use this with `GET` requests.\n\n```js\nzlFetch('some-url', {\n  queries: {\n    param1: 'value1',\n    param2: 'to encode',\n  },\n})\n\n// The above request can be written in Fetch like this:\nfetch('url?param1=value1\u0026param2=to%20encode')\n```\n\n### `Content-Type` generation based on `body` content\n\nzlFetch sets `Content-Type` appropriately depending on your `body` data. It supports three kinds of data:\n\n- Object\n- Query Strings\n- Form Data\n\nIf you pass in an `object`, zlFetch will set `Content-Type` to `application/json`. It will also `JSON.stringify` your body so you don't have to do it yourself.\n\n```js\nzlFetch.post('some-url', {\n  body: { message: 'Good game' },\n})\n\n// The above request is equivalent to this\nfetch('some-url', {\n  method: 'post',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ message: 'Good game' }),\n})\n```\n\nzlFetch contains a `toObject` helper that lets you convert Form Data into an object. This makes it super easy to zlFetch with forms.\n\n```js\nimport { toObject } from 'zl-fetch'\nconst data = new FormData(form.elements)\n\nzlFetch('some-url', {\n  body: toObject(data),\n})\n```\n\nIf you pass in a string, zlFetch will set `Content-Type` to `application/x-www-form-urlencoded`.\n\nzlFetch also contains a `toQueryString` method that can help you convert objects to query strings so you can use this option easily.\n\n```js\nimport { toQueryString } from 'zl-fetch'\n\nzlFetch.post('some-url', {\n  body: toQueryString({ message: 'Good game' }),\n})\n\n// The above request is equivalent to this\nfetch('some-url', {\n  method: 'post',\n  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n  body: 'message=Good%20game',\n})\n```\n\nIf you pass in a Form Data, zlFetch will let the native `fetch` function handle the `Content-Type`. Generally, this will use `multipart/form-data` with the default options. If you use this, make sure your server can receive `multipart/form-data`!\n\n```js\nimport { toObject } from 'zl-fetch'\nconst data = new FormData(form.elements)\n\nzlFetch('some-url', { body: data })\n\n// The above request is equivalent to this\nfetch('some-url', { body: data })\n\n// Your server should be able to receive multipart/form-data if you do this. If you're using Express, you can a middleware like multer to make this possible:\nimport multer from 'multer'\nconst upload = multer()\napp.use(upload.array())\n```\n\n**Breaking Change in `v5.0.0`**: If you pass in a `Content-Type` header, zlFetch will not set format your body content anymore. We expect you to be able to pass in the correct data type. (We had to do this to support the new API mentioned above).\n\n### Authorization header helpers\n\nIf you provide zlFetch with an `auth` property, it will generate an Authorization Header for you.\n\nIf you pass in a `string` (commonly for tokens) , it will generate a Bearer Auth.\n\n```js\nzlFetch('some-url', { auth: 'token12345' })\n\n// The above request can be written in Fetch like this:\nfetch('some-url', {\n  headers: { Authorization: `Bearer token12345` },\n})\n```\n\nIf you pass in an `object`, zlFetch will generate a Basic Auth for you.\n\n```js\nzlFetch('some-url', {\n  auth: {\n    username: 'username'\n    password: '12345678'\n  }\n})\n\n// The above request can be written in Fetch like this:\nfetch('some-url', {\n  headers: { Authorization: `Basic ${btoa('username:12345678')}` }\n});\n```\n\n### Creating a zlFetch Instance\n\nYou can create an instance of `zlFetch` with predefined options. This is super helpful if you need to send requests with similar `options` or `url`.\n\n- `url` is required\n- `options` is optional\n\n```js\nimport { createZLFetch } from 'zl-fetch'\n\n// Creating the instance\nconst api = zlFetch(baseUrl, options)\n```\n\nAll instances have shorthand methods as well.\n\n```js\n// Shorthand methods\nconst response = api.get(/* ... */)\nconst response = api.post(/* ... */)\nconst response = api.put(/* ... */)\nconst response = api.patch(/* ... */)\nconst response = api.delete(/* ... */)\n```\n\nNew in `v5.0.0`\n\nYou can now use a `zlFetch` instance without passing a URL. This is useful if you have created an instance with the right endpoints.\n\n```js\nimport { createZLFetch } from 'zl-fetch'\n\n// Creating the instance\nconst api = zlFetch(baseUrl, options)\n```\n\nAll instances have shorthand methods as well.\n\n```js\n// Shorthand methods\nconst response = api.get() // Without URL, without options\nconst response = api.get('some-url') // With URL, without options\nconst response = api.post('some-url', { body: 'message=good+game' }) // With URL, with options\nconst response = api.post({ body: 'message=good+game' }) // Without URL, with options\n```\n\n### Custom response handler\n\nIf you want to handle a response not supported by zlFetch, you can pass `customResponseParser: true` into the options. This returns the response from a normal Fetch request without any additional treatments from zlFetch. You can then use `response.json()` or other methods as you deem fit.\n\n```js\nconst response = await zlFetch('url', {\n  customResponseParser: true,\n})\nconst data = await response.arrayBuffer()\n```\n\n## Streaming with event source\n\nWe created a small wrapper around [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) for streaming with SSE.\n\nOn the browser, we use the Browser's Event Source — with a few addons — as the default. On the server, we wrap zlFetch with a retry functionality to make it similar to the Browser version.\n\n```js\nimport { zlEventSource } from 'zl-fetch'\nconst source = zlEventSource(url, options)\n```\n\n### Listen to any event\n\nzlEventSource lets you listen to any events by providing the event as a callback in `options`. Custom events are also supported. This provides a simpler API for usage.\n\n```js\nimport { zlEventSource } from 'zl-fetch'\nconst source = zlEventSource(url, {\n  open: data =\u003e console.log(data),\n  message: data =\u003e console.log(data),\n  ping: data =\u003e console.log(data), // This is a custom event\n  close: data =\u003e console.log(data),\n})\n```\n\n### Using the Fetch Version\n\nThe browser's event source capabilities are quite limited — you can only send a `GET` request. If you want to be able to send `POST` requests, add `Authorization`, the best method is to use the wrapped zlFetch version.\n\nTo use this, just set `useFetch` to true.\n\nOn servers, we automatically use the wrapped zlFetch version.\n\n```js\nimport { zlEventSource } from 'zl-fetch'\nconst source = zlEventSource(url, { useFetch: true }, fetchOptions)\n```\n\nYou can continue monitoring for events with the event callbacks.\n\n```js\nimport { zlEventSource } from 'zl-fetch'\nconst source = zlEventSource(\n  url,\n  {\n    useFetch: true,\n    message: data =\u003e console.log(data),\n  },\n  fetchOptions,\n)\n```\n\n#### Setting Retry Interval\n\nRetry intervals will be set according to the `retry` property sent in the SSE response. If it's not present, you can adjust the retry interval with the `retry` property.\n\n```js\nimport { zlEventSource } from 'zl-fetch'\nconst source = zlEventSource(\n  url,\n  {\n    useFetch: true,\n    retry: 3000, // In milliseconds\n  },\n  fetchOptions,\n)\n```\n\n### Closing an event source\n\n`zlEventSource` will close automatically if the server sents a `close` event. If you wish to terminate the session earlier, you can call the `close` method on the event source.\n\n```js\nconst source = zlEventSource(url)\nsource.close() // Terminates the event source connection\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzellwk%2Fzl-fetch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzellwk%2Fzl-fetch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzellwk%2Fzl-fetch/lists"}