Skip to content

๐Ÿ’ป๐ŸŒ๐Ÿ› ๏ธ Experience an upgraded fetch API ๐ŸŒ. Operates seamlessly on node, browsers, and workers.

License

Notifications You must be signed in to change notification settings

nyxblabs/fetchwizard

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

9 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

cover npm version npm downloads bundle JSDocs License

๐Ÿง™โ€โ™‚๏ธ fetchwizard

Experience an upgraded fetch API ๐ŸŒ. Operates seamlessly on node, browsers, and workers ๐Ÿ’ป๐ŸŒ๐Ÿ› ๏ธ.

๐Ÿ•’ Quick Start

Install:

# nyxi
nyxi fetchwizard

#pnpm
pnpm add fetchwizard

# npm
npm i fetchwizard

# yarn
yarn add fetchwizard

Import:

// ESM / Typescript
import { fetchwizard } from 'fetchwizard'

// CommonJS
const { fetchwizard } = require('fetchwizard')

โš™๏ธ Works with Node.js

We use conditional exports to detect Node.js and automatically use nyxblabs/fetch-for-all. If globalThis.fetch is available, it will be used instead. To leverage Node.js 17.5.0 experimental native fetch API, use the ๐Ÿ”ฌ --experimental-fetch flag.

โณ keepAlive support

By setting the FETCH_KEEP_ALIVE environment variable to true, an โšก๏ธ http/https agent will be registered that keeps sockets around even when there are no outstanding requests, so they can be used for future requests without having to reestablish a TCP connection.

Note: This option can potentially introduce memory leaks. Please check node-fetch/node-fetch#1325.

๐Ÿ“š Parsing Response

fetchwizard will smartly parse JSON and native values using nyxjson, falling back to text if it fails to parse.

const { users } = await fetchwizard('/api/users')

For binary content types, fetchwizard will instead return a ๐Ÿ“ฆ Blob object.

You can optionally provide a different parser than destr, or specify ๐Ÿ“ฆ blob, ๐Ÿงฑ arrayBuffer or ๐Ÿ“ text to force parsing the body with the respective FetchResponse method.

// Use JSON.parse
await fetchwizard('/movie?lang=en', { parseResponse: JSON.parse })

// Return text as is
await fetchwizard('/movie?lang=en', { parseResponse: txt => txt })

// Get the blob version of the response
await fetchwizard('/api/generate-image', { responseType: 'blob' })

๐Ÿ“ JSON Body

fetchwizard automatically stringifies the request body (if an object is passed) and adds JSON ๐Ÿ“‹ Content-Type and ๐Ÿ“ฅ Accept headers (for put, patch, and post requests).

const { users } = await fetchwizard('/api/users', { method: 'POST', body: { some: 'json' } })

โŒ Handling Errors

fetchwizard automatically throws errors when response.ok is false with a friendly error message and compact stack (hiding internals).

Parsed error body is available with error.data. You may also use the FetchError type.

await fetchwizard('http://google.com/404')
// FetchError: 404 Not Found (http://google.com/404)
//     at async main (/project/playground.ts:4:3)

In order to bypass errors as response you can use error.data:

await fetchwizard(...).catch((error) => error.data)

๐Ÿ” Auto Retry

fetchwizard automatically retries the request if an error occurs. The default number of retries is 1 (except for POST, PUT, PATCH, and DELETE methods, which have a default of 0 retries).

await fetchwizard('http://google.com/404', {
   retry: 3
})

โœจ Type Friendly

Responses can be type-assisted:

const article = await fetchwizard<Article>(`/api/article/${id}`)
// Auto complete working with article.id

๐ŸŒ Adding baseURL

By using the baseURL option, fetchwizard prepends it while respecting trailing/leading slashes and query search parameters for baseURL using url-ops:

await fetchwizard('/config', { baseURL })

๐Ÿ” Adding Query Search Params

By using the query option (or params as an alias), fetchwizard adds query search parameters to the URL by preserving the query in the request itself using url-ops:

await fetchwizard('/movie?lang=en', { query: { id: 123 } })

โšก๏ธ Interceptors

It is possible to provide async interceptors to hook into the lifecycle events of fetchwizard calls.

You might want to use fetchwizard.create to set shared interceptors.

๐Ÿš€ onRequest({ request, options })

The onRequest interceptor is called as soon as fetchwizard is being called, allowing you to modify options or perform simple logging.

await fetchwizard('/api', {
   async onRequest({ request, options }) {
      // Log request
      console.log('[fetch request]', request, options)

      // Add `?t=1640125211170` to query search params
      options.query = options.query || {}
      options.query.t = new Date()
   }
})

โŒ onRequestError({ request, options, error })

The onRequestError interceptor will be called when the fetch request fails.

await fetchwizard('/api', {
   async onRequestError({ request, options, error }) {
      // Log error
      console.log('[fetch request error]', request, error)
   }
})

โœ… onResponse({ request, options, response })

The onResponse interceptor will be called after the fetch call and parsing the response body.

await fetchwizard('/api', {
   async onResponse({ request, response, options }) {
      // Log response
      console.log('[fetch response]', request, response.status, response.body)
   }
})

โŒ onResponseError({ request, options, response })

The onResponseError interceptor is similar to onResponse, but it will be called when the fetch request is successful (response.ok is not true).

await fetchwizard('/api', {
   async onResponseError({ request, response, options }) {
      // Log error
      console.log('[fetch response error]', request, response.status, response.body)
   }
})

๐Ÿ”ง Create fetch with default options

This utility is useful if you need to use common options across several fetch calls.

Note: Defaults will be cloned at one level and inherited. Be careful with nested options like headers.

const apiFetch = fetchwizard.create({ baseURL: '/api' })

apiFetch('/test') // Same as fetchwizard('/test', { baseURL: '/api' })

๐Ÿ“ Adding headers

By using the headers option, fetchwizard adds extra headers in addition to the default headers of the request:

await fetchwizard('/movies', {
   headers: {
      'Accept': 'application/json',
      'Cache-Control': 'no-cache'
   }
})

๐ŸŒ Adding HTTP(S) Agent

If you need to use an HTTP(S) Agent, you can add the agent option with https-proxy-agent (for Node.js only):

import { HttpsProxyAgent } from 'https-proxy-agent'

await fetchwizard('/api', {
   agent: new HttpsProxyAgent('http://example.com')
})

๐Ÿ” Access to Raw Response

If you need to access the raw response (for headers, etc.), you can use fetchwizard.raw:

const response = await fetchwizard.raw('/sushi')

// response._data
// response.headers
// ...

๐Ÿš€ Native fetch

As a shortcut, you can use fetchwizard.native which provides the native fetch API.

const json = await fetchwizard.native('/sushi').then(r => r.json())

๐Ÿ“ฆ Bundler Notes

  • โœจ All targets are exported with Module and CommonJS format and named exports
  • โš™๏ธ No export is transpiled for the sake of modern syntax
    • ๐Ÿ”„ You probably need to transpile fetchwizard, destr, and ufo packages with babel for ES5 support
  • ๐Ÿ“ฅ You need to polyfill fetch global for supporting legacy browsers like using unfetch

โ“ FAQ

โ” Why export is called fetchwizard instead of fetch?

Using the same name of fetch can be confusing since API is different but still it is a fetch so using closest possible alternative. You can however, import { fetch } from fetchwizard which is auto polyfilled for Node.js and using native otherwise. ๐Ÿ”€

โ” Why not having default export?

Default exports are always risky to be mixed with CommonJS exports. โš ๏ธ

This also guarantees we can introduce more utils without breaking the package and also encourage using fetchwizard name. ๐Ÿ“ฆ

โ” Why not transpiled?

By keep transpiling libraries we push web backward with legacy code which is unneeded for most of the users. โฎ๏ธ

If you need to support legacy users, you can optionally transpile the library in your build pipeline. โš™๏ธ

๐Ÿ“œ License

MIT - Made with ๐Ÿ’ž

About

๐Ÿ’ป๐ŸŒ๐Ÿ› ๏ธ Experience an upgraded fetch API ๐ŸŒ. Operates seamlessly on node, browsers, and workers.

Topics

Resources

License

Code of conduct

Stars

Watchers

Forks

Sponsor this project