Skip to content

A fetch API polyfill for React Native with text streaming support.

License

Notifications You must be signed in to change notification settings

idanmosh/fetch

Repository files navigation

Fetch

This is a fork of GitHub's fetch polyfill, the fetch implementation React Native currently provides. This project features an alternative fetch implementation directy built on top of React Native's networking primitives instead of XMLHttpRequest for performance gains. At the same time, it aims to fill in some gaps of the WHATWG specification for fetch, namely the support for text streaming.

In practice, functionality-wise, this implementation is a drop-in replacement to GitHub's polyfill as it closely follows its implementation. Do not use this implementation if your application does not require to stream text responses.

Motivation

GitHub's fetch polyfill, originally designed with the intention to be used in web browsers without support for the fetch standard, most notably does not support the consumption of a response body as a stream.

However, as React Native does not yet provide direct access to the underlying byte stream for responses, we either have to fallback to XMLHttpRequest or React Native's networking API for iOS and Android. Currently, only strings can be transfered through the bridge, thus binary data has to be base64-encoded (source) and while React Native's XHR provides progress events to receive incremental data, it concatenates the response string as data comes in. Although very inefficient, the response can be sliced up and each chunk encoded into its UTF-8 representation with TextEncoder.

Instead of relying on XMLHttpRequest which degrades performance, we remove it out of the equation and have fetch invoke and control RCTNetworking API directly instead. To make Response.body work, ReadableStream's controller was integrated with RCTNetworking's progress events. It's important to stress that progress events are only dispatched when the native response type is set to text (https://github.com/facebook/react-native/blob/v0.63.4/Libraries/Network/RCTNetworking.mm#L544-L547), therefore limiting streaming to text-only transfers. If you wish to consume binary data, either blob or base64 response types have to be used. In this case, the downside is that the final response body is read as a whole and enqueued to the stream's controller as a single chunk. There is no way to read a partial response of a binary transfer.

For more context, read the following:

Requirements

This implementation depends on the following web APIs which are not currently available in React Native:

It should be possible remove the dependency on TextEncoder and TextDecoder, but not on ReadableStream. Either way, beware the bundle size of your application will inevitable increase.

To polyfill the above APIs, use react-native-polyfill-globals.

Install

$ npm install @react-native-community/fetch --save

Setup

The APIs provided by GitHub's implementation in React Native have to be replaced by those provided by this implementation. To do so, check and install react-native-polyfill-globals and follow the instructions therein.

Usage

No need to import anything after the setup is done. All APIs will be available globally.

Example:

fetch('https://jsonplaceholder.typicode.com/todos/1')
  .then(response => response.json())
  .then(json => console.log(json))

Check fetch's official documentation to learn more about the concepts and extended usage.

Aborting requests

It's possible to abort an on-going request and React Native already supports AbortController, so there is no need for a polyfill.

const controller = new AbortController();

fetch('https://jsonplaceholder.typicode.com/todos/1', { signal: controller.signal })
  .then(response => response.json())
  .then(json => console.log(json))

Learn more about abortable fetch at

Cookies

There is no concept of Cross-Origin Resource Sharing (CORS) in native apps. React Native only accepts a boolean value for the credentials option. As such, to send cookies you can either use same-origin and include.

The Set-Cookie response header returned from the server is a [forbidden header name][https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name] and therefore can't be programmatically read with response.headers.get(). Instead, the platform's native networking stack automatically manages cookies for you.

If you run into issues with cookie-based authentication, read the following:

Alternatively, you may consider using the react-native-cookies.

Request caching directive

The only values supported for the cache option are no-cache and no-store and Both achieve exactly the same result. All other values are ignored. Following GitHub's implementation, a cache-busting mechanism is provided by using the query parameter _ which holds the number of milliseconds elapsed since the Epoch when either no-cache or no-store are specified.

Redirect modes directive

The fetch specification defines these values for the redirect option: follow (the default), error, and manual. React Native does not accept such option but it does transparently follow a redirect response given the Location header for 30x status codes.

Tests

To run the test suite, you must use react-native-test-runner CLI. Run the run-tests.js wrapper script to spin up a local HTTP server to execute the networking tests against.

iOS

$ ./run-tests.js --platform ios --simulator '<simulator>' test/index.js 

Where <simulator> can be a combination of a device type and iOS version, e.g. iPhone 11 (14.1), or a device UUID. Check which simulators are available in your system by running the following command:

$ xcrun xctrace list devices

Android

$ ./run-tests.js --platform android --emulator '<emulator>' test/index.js 

Where <emulator> is the name of the Android Virtual Device (AVD), e.g. Pixel_API_28_AOSP. Check which emulators are available in your system by running the following command:

$ emulator -list-avds

About

A fetch API polyfill for React Native with text streaming support.

Resources

License

Code of conduct

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • JavaScript 100.0%