leonardodino / ya-fetch

Tiny wrapper around fetch

Home Page:https://bundlephobia.com/result?p=ya-fetch

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ya-fetch

Super light-weight wrapper around fetch

  • Only 822 B when minified & gziped
  • Only native API (polyfills for fetch, AbortController required)
  • TypeScript support
  • Instance with custom defaults
  • Methods shortcuts
  • Response type shortcuts
  • First class JSON support
  • Search params
  • Timeouts
  • Progress tracking
  • Retry
  • Zero deps

πŸ“¦ Install

$ yarn add ya-fetch
import YF from 'ya-fetch'

// inside an aync function
const result = await YF.patch('http://example.com/posts', {
  params: { id: 1 },
  json: { title: 'New Post' },
}).json()

console.log(result)
// β†’ { userId: 1, id: 1, title: 'New Post', body: 'Some text', }

πŸ’» Usage

Create instance

// api.js
import YF from 'ya-fetch'

const api = YF.create({
  prefixUrl: 'https://jsonplaceholder.typicode.com',
  headers: {
    Authorization: 'Bearer 943b1a29b46248b29336164d9ec5f217',
  },
})

export default api

Search params

import api from './api'

api.get('/posts', { params: { userId: 1 } }).json()
Same code without wrapper
fetch('http://example.com/posts?id=1').then((res) => {
  if (res.ok) {
    return res.json()
  }

  throw new Error('Oops')
})

Send & receive JSON

import api from './api'

api.post('/posts', { json: { title: 'New Post' } }).json()
Same code without wrapper
fetch('http://example.com/posts', {
  method: 'POST',
  headers: {
    'content-type': 'application/json',
    accept: 'application/json',
  },
  body: JSON.stringify({ title: 'New Post' }),
}).then((res) => {
  if (res.ok) {
    return res.json()
  }

  throw new Error('Oops')
})

Timeout

Cancel request if it is not fulfilled in period of time.

import { isTimeoutError } from 'ya-fetch'
import api from './api'

api
  .get('/posts', { timeout: 300 })
  .json()
  .then((posts) => console.log(posts))
  .catch((error) => {
    if (isTimeoutError(error)) {
      // do something
    }
  })
Same code without wrapper
const controller = new AbortController()

setTimeout(() => {
  controller.abort()
}, 300)

fetch('http://example.com/posts', {
  signal: controller.signal,
  headers: {
    accept: 'application/json',
  },
})
  .then((res) => {
    if (res.ok) {
      return res.json()
    }

    throw new Error('Oops')
  })
  .catch((error) => {
    if (error.name === 'AbortError') {
      // do something
    }
  })

Cancel request

This feature may require polyfill for AbortController and fetch.

import { isAbortError } from 'ya-fetch'
import { useEffect, useState } from 'react'
import api from './api'

export function usePosts() {
  const [posts, setPosts] = useState([])

  useEffect(() => {
    const controller = new AbortController()

    api
      .get('/posts', { signal: controller.signal })
      .json()
      .then((data) => setPosts(data))
      .catch((error) => {
        if (isAbortError(error)) {
          // do something
        }
      })

    return () => controller.abort()
  }, [setPosts])

  return posts
}

Provide custom search params serializer

By default parsed & stringified with URLSearchParams.

import YF from 'ya-fetch'
import queryString from 'query-string'

const api = YF.create({
  prefixUrl: 'https://jsonplaceholder.typicode.com',
  serializer(params) {
    return queryString.stringify(params, { arrayFormat: 'bracket' })
  },
})

api.get('/posts', { params: { userId: 1, tags: [1, 2] } })
// https://jsonplaceholder.typicode.com/posts?userId=1&tags[]=1&tags[]=2

πŸ“– API

Instance

type Request = (resource: string, options?: Options) => ResponseBody

interface Instance extends Request {
  create(options?: Options): Instance
  extend(options?: Options): Instance
  options: Options
  get: Request
  post: Request
  put: Request
  patch: Request
  head: Request
  delete: Request
}

Options

interface Options extends RequestInit {
  /** Object that will be stringified with `JSON.stringify` */
  json?: unknown
  /** Object that can be passed to `serialize` */
  params?: Record<string, any>
  /** Throw `TimeoutError`if timeout is passed */
  timeout?: number
  /** String that will prepended to `resource` in `fetch` instance */
  prefixUrl?: string
  /** Request headers */
  headers?: Record<string, string>
  /** Custom params serializer, default to `URLSearchParams` */
  serialize?(params: Record<string, any>): URLSearchParams | string
  /** Response handler, must handle status codes or throw `ResponseError` */
  onResponse?(
    response: Response,
    options: Options
  ): Response | Promise<Response> | never | Promise<never>
  /** Response handler with sucess status codes 200-299 */
  onSuccess?(response: Response, options: Options): Response | Promise<Response>
  /** Error handler, must throw an `Error` */
  onFailure?(
    error: ResponseError | AbortError | TimeoutError | Error,
    options: Options
  ): never | Promise<never>
  /** Transform parsed JSON from response */
  onJSON?(input: unknown): unknown
}

ResponseBody

interface ResponseBody extends Promise<Response> {
  json?<T>(): Promise<T>
  text?(): Promise<string>
  blob?(): Promise<Blob>
  arrayBuffer?(): Promise<ArrayBuffer>
  formData?(): Promise<FormData>
}

πŸ”— Alternatives

  • ky - Library that inspired this one, but 3x times bigger and not transpiled for es5 browsers
  • axios - Based on old XMLHttpRequests API, almost 5x times bigger, but super popular and feature packed

MIT Β© John Grishin

About

Tiny wrapper around fetch

https://bundlephobia.com/result?p=ya-fetch

License:MIT License


Languages

Language:TypeScript 92.7%Language:Handlebars 5.6%Language:JavaScript 1.8%