npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@nrk/rfetch

v1.1.0

Published

rfetch - fetch with retry

Downloads

51

Maintainers

stiansstianslorecasterlorecasternrk-ps-teamcitynrk-ps-teamcityswlaswlanrk-midas-jenkinsnrk-midas-jenkinsandorpandorandorpandornrkrichardnrkrichardgesigesigundelsby-nrkgundelsby-nrkjonstalecarlsenjonstalecarlsenmslhmmslhmnrk-sofie-cinrk-sofie-cinytaminnytaminjesperstarkarjesperstarkarskjalgepalgskjalgepalgeirikhalvardeirikhalvardastokkeastokken640071n640071jfjeldskaarjfjeldskaarsondrbwsondrbwhenrik-mattssonhenrik-mattssonolapeterolapeterdervodevdervodevstefanogdennrkstefanogdennrkhaavardmhaavardmyryrnrk-kurator-jenkinsnrk-kurator-jenkinstorgeilotorgeilonrk-user-syncnrk-user-syncdhdeploydhdeployespenwaespenwaovstetunovstetunstianljstianljharaldkjharaldkjmariusumariusucristobalcristobalknuthaugknuthaugthohalvthohalvjohnarnejohnarneeshaswinieshaswinimorrowmorrowoyvindehoyvindehlaatlaattoggutoggunrk-jenkinsnrk-jenkinszenangstzenangstplommaplommaevjandevjandmoltubakkmoltubakkmariehovlandmariehovlandingridgureningridgurenlu-luxlu-luxanderslianderslisiljesiljestiandgstiandgsjurlursjurlurandipodnrkandipodnrkpkejpkejyosrimtiyosrimtimorten.nyhaugmorten.nyhaugingvildcathingvildcatherlend.joneserlend.jonesbrneirikbrneirikmollersemollersetbnrktbnrknordankenordankesimonmitternachtsimonmitternachtmartintorgersenmartintorgersenrebchrrebchrsteipalsteipalcarinafraningcarinafraningdiscobusdiscobuseirikjstnrkeirikjstnrkmartingundersenmartingundersentinkajtstinkajtshallvardlidhallvardlidtomivartomivarajacoajacotobinustobinusmortenokmortenokfeiringfeiringnrk-ark-deploynrk-ark-deployjeangilbertlouisjeangilbertlouisheidimorkheidimorkingriddraageningriddraagenfridajalborgfridajalborgbruusibruusirosvollrosvollchristianeidechristianeideenordbyenordbyglen_imrieglen_imriemia.aasbakkenmia.aasbakkenelathamnaelathamnaevjjan17evjjan17olatoftolatoftkongsrudkongsrudchrpeterchrpeteringvildforsethingvildforsethharaldk76haraldk76stigokstigokjohannesodlandjohannesodlandanders993anders993vildefjvildefjvildepkvildepkhelenperhelenperrolerbolerrolerbolermeloyguttmeloyguttanders.baggethunanders.baggethunespenhalstensenespenhalstensenandreeldareideandreeldareideytterboytterbovagifabilovvagifabilovhermangudesenhermangudesenhaakemonhaakemonhenningkollerhenningkollerteodor-elstadteodor-elstaddanjohnrkdanjohnrkopetopetandrefauandrefaumadsernmadserncbjerkancbjerkankariaankariaangardkroyergardkroyermikkelnygardmikkelnygardsiddisesiddiseklizterklizteredplayzedplayztrulsltrulslmiatollaksvikmiatollaksvikmachineboycommachineboycomclaudio-nrkclaudio-nrkgrimburgrimburn07073n07073theasparrowhawktheasparrowhawkshieldyarmorshieldyarmoremte123emte123supermeisensupermeisentoshbtoshbarevjensenarevjensenbaltebalteeschoieneschoienmaar1052maar1052ragnaroh-nrkragnaroh-nrkrannveigncrannveigncharaldskharaldskluminrkluminrkeskilgheskilghtorsrextorsrexthormodbthormodbjoakimmohnjoakimmohnebrautasetebrautasetkjellovenordlienkjellovenordlienjanerikbrjanerikbrgunderwondergunderwonderjulusianjulusiandaardaldaardalmmepmmepjimalexbergerjimalexbergersiiverssiiversmuddahmuddahihlnrkihlnrkmorten-nrkmorten-nrkkristjgrkristjgrhenrkhenrkknutboknutbosokkemannensokkemannensanderknrksanderknrkjensragejensragekjellvnnrkkjellvnnrknicklassvendsrudnicklassvendsrudellenulrikellenulrikjorn_georgjorn_georgingryeeingryeehalvorhhalvorhphajsiphajsimartioskmartiosktobiasrptobiasrpjimmeloysundjimmeloysundlarsjorgentvedtlarsjorgentvedtkjartanmichalsenkjartanmichalsenmariusenerlynrkmariusenerlynrkkennethdammyrkennethdammyrnikolai.kjarem.ellingsennikolai.kjarem.ellingsentekbeartekbear

Keywords

Readme

RFetch

Fetch With Retry

Installation

node:

$ npm install @nrk/rfetch

Requirements (Dependencies and/or Polyfills)

By Default we do not install any dependencies, so you will have to install the required dependencies and/or polyfills needed for the intended target(s) of your choice below.

Node

For node environment you will need to install the following dependencies:

$ npm install node-fetch
$ npm install abort-controller

Browser

For web browser environment target you will need to have to polyfill the following builtin and API's if not present:

NOTE: You may want to only polyfill only what is needed depending on your target group browser compatibility needs.

Isomorphic

If you intend to develop an isomorphic app where you target both the web and node targets, you will need to address the depencies litst above for each environment. Also you may want to prefer to install the isomorphic-fetch, which both includes node-fetch and whatwg-fetch.

Bundlers such as browserify, rollup and webpack will lookup the main, module and browser fields in the package.json to lookup the corresponding file to use for the given target either it be node or browser.

$ npm install isomorphic-fetch
$ npm install abort-controller

Dist

A minified umd module for web, is provided under the dist folder, as dist/rfetch.web.min.js with a corresponding source map file. It exports rfetch on to the window

Usage

const rfetch = require('@nrk/rfetch')
const url = 'http://example.com/api/endpoint'

/*
  For a more comprenhensive API reference for the fetchOptions see:

  - https://github.github.io/fetch/
  - https://github.com/bitinn/node-fetch
*/
const fetchOptions = {
  method: 'GET',
  ...
}

const retryOptions = {
  /*
   Optional signalTimeout in ms, how long before each fetch request should timeout.
   The default timeout is 1000ms = 1 second.
  */
  signalTimeout: 1000,

  /*
    Optional resolveOn, can be a single http status code or various http status codes.
    In most use cases we would want to resolve on one given http status.
    The default status code we try to resolve on is http status = 200 OK.
  */
  resolveOn: 200,

  /*
    How many retries before failing.
    The default value is 3 retries to attempt to fetch given url.
  */
  retries: 3,

  /*
    Retry timeout, can be a single timeout number or various timeout numbers to loop over.
    The default value is 100ms, which is how long it should delay before retrying a new fetch request.
  */
  retryTimeout: 100,

  /*
    Which http status codes to retry on, can be a single http status code or various http status codes.
    The deault values is an empty set, and the retry heuristic will retry on every http status code that
    does not match the http status codes to match on.
   */
  retryOn: [],

  /*
    Optional context object for more advanced controls
  */
  context: {
    /*
      The abortController property will be set internally, and is an instance of AbortController.
      This allows you to call it's abort() method, and stop the entire retry fetch loop.
    */
    abortController: null,

    /*
      Optional errors array (by reference), that allows us to inspect all the error(s)
      that were thrown in the retry loop for further processing etc.
    */
    errors: [],

    /*
      Optional sink callback (by reference), that allows us to listen to some of the phases in the
      retry fetch loop and eventually do some code based on the different phases.
     */
    sink (phase, context) {
      if (phase === 'rfetch.fetch.(start|resolved|rejected)') {
        ...
      }
    }
  }
}

try {
  const response = await rfetch(options, fetchOptions, retryOptions)
  const data = await response.json()
} catch (err) {
  console.error(err)
}

Note: both the fetchOptions and retryOptions are optional, and the retry options will default to values explained above

The context.abortController is set internally by the retry fetch loop and allows you to abort the retry loop, since it will abort the current fetch request in the loop via it's assigned abort controller.

ResolveOn and RetryOn heuristics

The resolveOn and retryOn can be one or more http status codes respectively.

  1. Success example - expect response.status = 200 and no retry on value(s) set
const retryOptions = {
  retries: 3,
  resolveOn: 200
}

fetch -> http 503 -> retry
fetch -> http 408 -> retry
fetch -> http 200 -> success -> resolve
  1. Success example - expect response.status in [ 200, 204 ] and no retry on value(s) set
const retryOptions = {
  retries: 3,
  resolveOn: [ 200, 204 ]
}

fetch -> http 418 -> retry
fetch -> http 204 -> success -> resolve
  1. Success example - expect response.status = 200 and retry on = 503
const retryOptions = {
  retries: 3,
  resolveOn: 200,
  retryOn: 503
}

fetch -> http 503 -> retry
fetch -> http 200 -> success -> resolve
  1. Success example - expect response.status = 200 and retry on = [ 503, 408 ]
const retryOptions = {
  retries: 3,
  resolveOn: 200,
  retryOn: [ 503, 408 ]
}

fetch -> http 503 -> retry
fetch -> http 408 -> retry
fetch -> http 200 -> success -> resolve
  1. Failure example, expect esponse.status = 200 and retry on = [ 503, 408 ].
const retryOptions = {
  retries: 3,
  resolveOn: 200,
  retryOn: [ 503, 408 ]
}

fetch -> http 503 -> retry
fetch -> http 418 -> failure -> reject

Retry timeout heuristics

The retryTimeout can either be a single timeout value that will be used as the default delay before starting a new fetch retry request. Or it can be a set of timeout values that we loop over e.g.:

const retryOptions = {
  retries: 5,
  retryTimeout: [100, 200]
}

fetch -> failure -> delay 100
fetch -> failure -> delay 200
fetch -> failure -> delay 100
fetch -> failure -> delay 200
fetch ...

Or you can specify for each retry:

const retryOptions = {
  retries: 4,
  retryTimeout: [100, 250, 300]
}

fetch -> failure -> delay 100
fetch -> failure -> delay 250
fetch -> failure -> delay 300
fetch ...

Signal and aborting the fetch retry loop

The following code will not work as expected e.g.:

let abortController
cancelButton.addEventListener('click',
  // this will not work as expected
  () => abortController.abort()
)

dataButton.addEventLister('click', async () => {
  abortController = new AbortController()
  const fetchOptions = {
    signal: abortController.signal
  }

  try {
    const response = await rfetch('/api/data', fetchOptions)
    ....
  } catch (e) {
    // will never be aborted by the abortController if the user click's the abort button
  }
})

The retry fetch loop will not be aborted (rejected) by the user input as one would expect. If you need to cancel the retry fetch loop you need to pass a context object with the retryOptions e.g.:

const retryOptions = {
  /* by reference */
  context: {}
}

cancelButton.addEventListener('click',
  () => retryOptions.context.abortController.abort()
)

dataButton.addEventLister('click', async () => {
  try {
    const response = await rfetch('/api/data', {}, retryOptions)
    ....
  } catch (e) {
     // will be avorted by the if the user click's the abort button
  }
})

Sink phases

You can pass a context object with the retryOptions, that allows you to pass a sink method by reference to listen to some of the phases in the retry fetch loop e.g.:

function sink (phase, context) {
  if (phase === 'rfetch.fetch.start') {
    console.log(`Start a fetch request url: <(${context.url})>, attempt: <${context.n}> of: <${context.retries}>.`)
  }

  if (phase === 'rfetch.fetch.rejected') {
    console.log(`Failure to fetch request url: <(${context.url})>, attempt: <${context.n}> of: <${context.retries}>, error: <${context.error.toString()}>.`)
  }

  if (phase === 'rfetch.fetch.resolved') {
    console.log(`Succeeded to fetch request url: <(${context.url})>, attempt: <${context.n}> of: <${context.retries}>.`)
  }
}

const url = 'http://example.com/api/endpoint'
const fetchOptions = {
  ...
}
const retryOptions = {
  context: {
    sink
  }
}


try {
  const response = await rfetch(url, fetchOptions, retryOptions)
} catch (error) {
  ...
}

Module Support

We support es6 modules e.g. the following code would also work:

import rfetch from '@nrk/rfetch'

const url = 'http://example.com/api/endpoint'
const fetchOptions = {
  ...
}
const retryOptions = {
  ...
}


try {
  const response = await rfetch(url, fetchOptions, retryOptions)
} catch (error) {
  ...
}

Bundlers such as browserify and rollup, webpack will lookup the corresponding .mjs file, also the latest node works has module support behind the --experimental-modules flag.