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

Overview

fetch

npm version ci

A fetch API polyfill for React Native with text streaming support

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 API 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, 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.

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, each chunk encoded into its UTF-8 representation with TextEncoder and finally enqueued to the stream.

Instead of relying on XMLHttpRequest, which degrades performance, we remove it out of the equation and have fetch interact with React Native's Networking API directly instead. To make Response.body work, ReadableStream's controller was integrated with native progress events. It's important to stress that progress events are only fired 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:

Related:

Requirements

React Native v0.62.0+ is the minimum version supported where the Networking API has been made public.

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-fetch-api --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.

Enable text streaming

A non-standard option was added to fetch to enable incremental events in React Native's networking layer.

fetch('https://jsonplaceholder.typicode.com/todos/1', { reactNative: { textStreaming: true } })
  .then(response => response.body)
  .then(stream => ...)

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 aborting fetch at https://developers.google.com/web/updates/2017/09/abortable-fetch.

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 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
Issues
  • fix: refactor BlobResponse and ArrayBufferResponse

    fix: refactor BlobResponse and ArrayBufferResponse

    closes #11

    simplifies implementation of both classes by extending Request and only supplying constructor and clone. Added some tests that are copy of existing tests with a clone call shoved in there. Haven't updated package.json or changelog yet

    opened by slightlytyler 8
  • Looking for collaborators

    Looking for collaborators

    @qalqi @cpojer @mislav @MattiasBuelens @ambarc @mythz If any of you are interested in contributing to this react-native-community/fetch effort, please let me know!

    For context on why this fork exists, see this PR.

    Also, if anyone reading this is interested, feel free to comment here. Thanks!

    opened by pcowgill 5
  • StreamBlobResponse should not return a promise

    StreamBlobResponse should not return a promise

    I'm working on integrating this polyfill to enable text streaming in my app and have encountered a problem that goes like this:

    fetch('...').then(response => {
        console.log(response.headers); // map object
        const clone = response.clone();
        console.log(clone.headers); // undefined, and headers isn't optional
        clone.then(cloneResponse => {
            console.log(cloneResponse.headers); // map object
        })
    })
    

    It seems like StreamBlobResponse constructor should not return a promise https://github.com/react-native-community/fetch/blob/master/src/StreamBlobResponse.js#L11-L22

    I experimented with removing the returns and that does seem to fix it. The implementation treats the respective class properties as optional so everything continues to work. Was this an oversight or is there a deeper reason that it's implemented like this which breaks the Response object?

    opened by slightlytyler 3
  • Implement response.body as a stream

    Implement response.body as a stream

    Moving this issue https://github.com/github/fetch/issues/746 here

    opened by pcowgill 2
  • Add CI

    Add CI

    opened by acostalima 0
  • Fix iOS job in GitHub workflow

    Fix iOS job in GitHub workflow

    Investigate why the iOS job is now failing with the following error:

    Invalid runtime: com.apple.CoreSimulator.SimRuntime.iOS-14-2
    

    The runtime in question was available before, so GitHub might have changed something in the latest macOS virtual environment.

    See: https://github.com/actions/virtual-environments/blob/main/images/macos/macos-11.0-Readme.md

    opened by acostalima 0
  • Fix iOS workflow

    Fix iOS workflow

    Changes

    • Tests on iOS platform now run in React Native 0.62.0 temporarily, i.e., until the iOS build on 0.63.4+ does not fail in the latest macOS.
    • Added cache for npm, Android build and iOS build.
    • Added badges to README.
    • Updated react-native-test-runner to 5.0.0.

    Fixes #9

    opened by acostalima 0
  • Get rid of the dependency on TextEncoder and TextDecoder

    Get rid of the dependency on TextEncoder and TextDecoder

    It should be possible to rewrite the code to not rely on both TextEncoder and TextDecoder, just like GitHub as done:

    • Decode: https://github.com/github/fetch/blob/a8aa427de0ed808ff26c0e3eb2e59c122c44488a/test/test.js#L79
    • Encode: https://github.com/github/fetch/blob/a8aa427de0ed808ff26c0e3eb2e59c122c44488a/test/test.js#L69

    By removing the dependency on both APIs, the app's bundle size can be further reduced. Considering that React Native's environment does not provide these APIs, there would be no need to polyfill them anymore. However, we should, by default, check whether TextEncoder and TextDecoder are available and only then fallback.

    enhancement help wanted 
    opened by acostalima 0
  • Use DOMException to throw errors

    Use DOMException to throw errors

    I believe React Native's environment does not provide DOMException, so we need to use a polyfill ~~or develop our own~~. Check domexpection. If that's the case, for consistency's sake, the polyfill should be delivered via react-native-polyfill-globals. Check GitHub's implementation for inspiration.

    DOMException should be thrown when:

    • fetch is aborted

    See: https://developers.google.com/web/updates/2017/09/abortable-fetch

    enhancement help wanted 
    opened by acostalima 0
  • Technical planning and discussion

    Technical planning and discussion

    @cpojer I'm reaching out to follow up on a previous discussion about coming up with a custom Fetch API implementation for React Native at https://github.com/react-native-community/fetch with support for streaming (https://github.com/facebook/react-native/issues/27741). We, MOXY and Protocol Labs, are planning to start this effort shortly and we'd like to know your thoughts on the matter.

    In short, what we currently have in mind is as follows:

    • Rewrite whatwg-fetch in modern JS.
    • Remove XHR out of the equation and implement fetch directly on top of RN's JavaScript Network API exactly for the same reasons outlined at https://github.com/react-native-community/discussions-and-proposals/issues/99.
    • Assume ReadableStream is available in RN's environment which can be polyfilled with web-streams-polyfill. If ReadableStream is expected to only be used in the context of Fetch, we can probably bundle them together.
    • Have tests in place running in an actual RN app on GitHub's CI.

    Even if Facebook has no plans to add support for streaming to Fetch for the time being, app developers can easily swap out whatwg-fetch for @react-native-community/fetch manually. Even better would be React Native to do this automatically via some sort of extension or plugin API.

    Are there any guidelines which React Native Community packages should conform to? e.g. linter and linting config, CI process config, etc.

    CC @satazor @hugomrdias

    opened by acostalima 13
Releases(v3.0.0)
Owner
React Native Community
Repositories supporting the React Native ecosystem
React Native Community
A react native version of backbone model

react-native-backbone As react native does not support Node.js HTTP module, react-native-backbone helps you to connect to your REST api or localStorag

Yi Wang 67 Oct 10, 2021
Use this library to efficiently download and upload blobs in React Native.

react-native-blob-courier Use this library to efficiently download and upload blobs in React Native. The library was inspired by rn-fetch-blob, and ai

Ely Deckers 71 Oct 9, 2021
Upload files in your React Native app even while it's backgrounded. Supports Android and iOS, including camera roll assets.

react-native-background-upload The only React Native http post file uploader with android and iOS background support. If you are uploading large files

Vydia 561 Oct 12, 2021
react-native module to download and upload files

This module is deprecated. Use https://github.com/wkh237/react-native-fetch-blob or https://github.com/johanneslumpe/react-native-fs instead. react-na

Erdem Başeğmez 105 Dec 23, 2020
Meteor Reactivity for your React Native application :)

react-native-meteor Meteor-like methods for React Native. If you have questions, you can open a new issue in the repository or ask in the our Gitter c

inProgress 707 Aug 29, 2021
Communicate over ad hoc wifi using Multipeer Connectivity

react-native-multipeer Communicate over ad hoc wifi using Multipeer Connectivity. Known Issues Below is a list of known issues. Pull requests are welc

Loch Wansbrough 181 Jul 22, 2021
lazyload for react native

react-native-lazyload A `lazyload` components suit for React Native. Install npm install react-native-lazyload Components Component Description Lazyl

Horcrux 405 Aug 9, 2021
SSDP client + server to use in your react-native apps

SSDP fork for React Native from node This is a fork of node-ssdp that uses react-native-udp instead of dgram to enable react multicast messaging and p

Netbeast 38 Sep 17, 2021
node's dgram for react-native

react-native-udp React Native UDP socket API for Android & iOS. It allows you to create UDP sockets, imitating Node's dgram API functionalities (check

Tradle 252 Oct 11, 2021
A React Native wrapper for AWS iOS/Android S3 SDK.

React Native AWS S3 A React Native wrapper for AWS iOS/Android S3 SDK. We currently implements TransferUtility, it allow you to upload / download task

MyBigDay 93 Aug 6, 2021
AWS Cognito integration module for React Native

React Native : AWS Cognito Module Deprecated! This library is not actively developed. Check out react-native-aws-cognito-js instead. react-native-cogn

Marcell Jusztin 63 Sep 25, 2018
Native file download utility for react-native

This repo is deprecated in favor of react-native-fs React Native File Download Native file download utility for react-native Note that does not suppor

Perry Poon 49 Jun 17, 2019
node's net api in react-native

TCP in React Native node's net API in React Native This module is used by Peel Install Create a new react-native project. Check react-native getting s

null 257 Oct 11, 2021
A plugin for file upload on react-native

react-native-file-upload React Native latest version had support file upload, the package is deprecated, detail see #4 #7. A file upload plugin for re

Liucw 114 Oct 10, 2020
React Native AWS Cognito JS SDK

This library is deprecated. It was merged into aws cognito identity js and finally into: https://github.com/aws-amplify/amplify-js. The amplify librar

Air 133 Jul 26, 2021
A React Native module to upload files and camera roll assets.

react-native-uploader A React Native module for uploading files and camera roll assets. Supports progress notification. Install iOS npm install react-

Adam Roth 514 Sep 18, 2021
OAuth login for React Native

react-native-simple-auth OAuth login for React Native Screencast Install Providers Setup Usage License Screencast iOS Android Source of example app: h

Adam Mcgrath 664 Oct 14, 2021
XMPP library for React Native (iOS and Android native support)

react-native-xmpp An XMPP library for React Native. A simple interface for native XMPP communication. Both iOS and Android are supported. Demo XmppDem

Pavel Aksonov 302 Oct 11, 2021
AWS SDK for React Native (developer preview)

Attention: Please use the AWS Amplify library for React Native development of new projects on AWS. We've also released a React Native starter kit at h

Amazon Archives 643 Sep 27, 2021