Coder Social home page Coder Social logo

fetch's Introduction

window.fetch polyfill

OpenSSF Scorecard

The fetch() function is a Promise-based mechanism for programmatically making web requests in the browser. This project is a polyfill that implements a subset of the standard Fetch specification, enough to make fetch a viable replacement for most uses of XMLHttpRequest in traditional web applications.

Table of Contents

Read this first

  • If you believe you found a bug with how fetch behaves in your browser, please don't open an issue in this repository unless you are testing in an old version of a browser that doesn't support window.fetch natively. Make sure you read this entire readme, especially the Caveats section, as there's probably a known work-around for an issue you've found. This project is a polyfill, and since all modern browsers now implement the fetch function natively, no code from this project actually takes any effect there. See Browser support for detailed information.

  • If you have trouble making a request to another domain (a different subdomain or port number also constitutes another domain), please familiarize yourself with all the intricacies and limitations of CORS requests. Because CORS requires participation of the server by implementing specific HTTP response headers, it is often nontrivial to set up or debug. CORS is exclusively handled by the browser's internal mechanisms which this polyfill cannot influence.

  • This project doesn't work under Node.js environments. It's meant for web browsers only. You should ensure that your application doesn't try to package and run this on the server.

  • If you have an idea for a new feature of fetch, submit your feature requests to the specification's repository. We only add features and APIs that are part of the Fetch specification.

Installation

npm install whatwg-fetch --save

You will also need a Promise polyfill for older browsers. We recommend taylorhakes/promise-polyfill for its small size and Promises/A+ compatibility.

Usage

Importing

Importing will automatically polyfill window.fetch and related APIs:

import 'whatwg-fetch'

window.fetch(...)

If for some reason you need to access the polyfill implementation, it is available via exports:

import {fetch as fetchPolyfill} from 'whatwg-fetch'

window.fetch(...)   // use native browser version
fetchPolyfill(...)  // use polyfill implementation

This approach can be used to, for example, use abort functionality in browsers that implement a native but outdated version of fetch that doesn't support aborting.

For use with webpack, add this package in the entry configuration option before your application entry point:

entry: ['whatwg-fetch', ...]

HTML

fetch('/users.html')
  .then(function(response) {
    return response.text()
  }).then(function(body) {
    document.body.innerHTML = body
  })

JSON

fetch('/users.json')
  .then(function(response) {
    return response.json()
  }).then(function(json) {
    console.log('parsed json', json)
  }).catch(function(ex) {
    console.log('parsing failed', ex)
  })

Response metadata

fetch('/users.json').then(function(response) {
  console.log(response.headers.get('Content-Type'))
  console.log(response.headers.get('Date'))
  console.log(response.status)
  console.log(response.statusText)
})

Post form

var form = document.querySelector('form')

fetch('/users', {
  method: 'POST',
  body: new FormData(form)
})

Post JSON

fetch('/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: 'Hubot',
    login: 'hubot',
  })
})

File upload

var input = document.querySelector('input[type="file"]')

var data = new FormData()
data.append('file', input.files[0])
data.append('user', 'hubot')

fetch('/avatars', {
  method: 'POST',
  body: data
})

Caveats

  • The Promise returned from fetch() won't reject on HTTP error status even if the response is an HTTP 404 or 500. Instead, it will resolve normally, and it will only reject on network failure or if anything prevented the request from completing.

  • For maximum browser compatibility when it comes to sending & receiving cookies, always supply the credentials: 'same-origin' option instead of relying on the default. See Sending cookies.

  • Not all Fetch standard options are supported in this polyfill. For instance, redirect and cache directives are ignored.

  • keepalive is not supported because it would involve making a synchronous XHR, which is something this project is not willing to do. See issue #700 for more information.

Handling HTTP error statuses

To have fetch Promise reject on HTTP error statuses, i.e. on any non-2xx status, define a custom response handler:

function checkStatus(response) {
  if (response.status >= 200 && response.status < 300) {
    return response
  } else {
    var error = new Error(response.statusText)
    error.response = response
    throw error
  }
}

function parseJSON(response) {
  return response.json()
}

fetch('/users')
  .then(checkStatus)
  .then(parseJSON)
  .then(function(data) {
    console.log('request succeeded with JSON response', data)
  }).catch(function(error) {
    console.log('request failed', error)
  })

Sending cookies

For CORS requests, use credentials: 'include' to allow sending credentials to other domains:

fetch('https://example.com:1234/users', {
  credentials: 'include'
})

The default value for credentials is "same-origin".

The default for credentials wasn't always the same, though. The following versions of browsers implemented an older version of the fetch specification where the default was "omit":

  • Firefox 39-60
  • Chrome 42-67
  • Safari 10.1-11.1.2

If you target these browsers, it's advisable to always specify credentials: 'same-origin' explicitly with all fetch requests instead of relying on the default:

fetch('/users', {
  credentials: 'same-origin'
})

Note: due to limitations of XMLHttpRequest, using credentials: 'omit' is not respected for same domains in browsers where this polyfill is active. Cookies will always be sent to same domains in older browsers.

Receiving cookies

As with XMLHttpRequest, 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, it's the browser's responsibility to handle new cookies being set (if applicable to the current URL). Unless they are HTTP-only, new cookies will be available through document.cookie.

Redirect modes

The Fetch specification defines these values for the redirect option: "follow" (the default), "error", and "manual".

Due to limitations of XMLHttpRequest, only the "follow" mode is available in browsers where this polyfill is active.

Obtaining the Response URL

Due to limitations of XMLHttpRequest, the response.url value might not be reliable after HTTP redirects on older browsers.

The solution is to configure the server to set the response HTTP header X-Request-URL to the current URL after any redirect that might have happened. It should be safe to set it unconditionally.

# Ruby on Rails controller example
response.headers['X-Request-URL'] = request.url

This server workaround is necessary if you need reliable response.url in Firefox < 32, Chrome < 37, Safari, or IE.

Aborting requests

This polyfill supports the abortable fetch API. However, aborting a fetch requires use of two additional DOM APIs: AbortController and AbortSignal. Typically, browsers that do not support fetch will also not support AbortController or AbortSignal. Consequently, you will need to include an additional polyfill for these APIs to abort fetches:

import 'yet-another-abortcontroller-polyfill'
import {fetch} from 'whatwg-fetch'

// use native browser implementation if it supports aborting
const abortableFetch = ('signal' in new Request('')) ? window.fetch : fetch

const controller = new AbortController()

abortableFetch('/avatars', {
  signal: controller.signal
}).catch(function(ex) {
  if (ex.name === 'AbortError') {
    console.log('request aborted')
  }
})

// some time later...
controller.abort()

Browser Support

  • Chrome
  • Firefox
  • Safari 6.1+
  • Internet Explorer 10+

Note: modern browsers such as Chrome, Firefox, Microsoft Edge, and Safari contain native implementations of window.fetch, therefore the code from this polyfill doesn't have any effect on those browsers. If you believe you've encountered an error with how window.fetch is implemented in any of these browsers, you should file an issue with that browser vendor instead of this project.

fetch's People

Contributors

bryanrsmith avatar chromakode avatar crorc avatar danez avatar dependabot[bot] avatar dgraham avatar github-actions[bot] avatar itsmattking avatar jakechampion avatar jamesplease avatar joaovieira avatar josh avatar joycebrum avatar kirill-konshin avatar kruppel avatar kwgithubusername avatar lrowe avatar matthew-andrews avatar mikemcquaid avatar mislav avatar nikhilm avatar othree avatar remcohaszing avatar roneythomas avatar scarlac avatar steveluscher avatar steveworkman avatar tamird avatar xymostech avatar zzau13 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fetch's Issues

Support IE8

Looking into using this. I unfortunately have to support IE8 still.

It looks like the current Browser Support for this polyfill is IE9+. Any plans for 8? (Or does the banner on Readme mean it probably will work on 8, but wasn't tested on it yet?)

Thank you!

Let JSON consume body.text

There's no particular reason that the json function can't consume the body as text and not error if the body has already been consumed by a .text call.

Use node ecosystem

Since you are already publishing to Bower, which requires node, I would suggest the following adjustments.

  • Expose your shim as a module.
  • Replace your shell script test runner with an environment that can run from within node
    • Suggest switching from qunit to mocha
    • Have a setup and teardown script to run your server for integration tests
  • For node users, have an implementation that exposes the API, but uses node's http, or a simple wrapper around request
  • Replace Makefile with npm tasks that run from package.json (example below)

Example package.json for scripts

{
  "private": true,
  "name": "fetch-client",
  "version": "0.2.1",
  "main": "./fetch.js"
  "scripts": {
    "post-install": "bower install"
    "browser-tests": "./test/run.sh",
    "lint": "jshint *.js test/*.js",
    "clean": "rimraf ./bower_components ./node_modules"
    "build": "npm run clean && npm install",
    "test": "npm run lint && npm run browser-tests"
  },
  "devDependencies": {
    "bower": "1.3.8",
    "jshint": "2.5.2",
    "node-qunit-phantomjs": "0.2.2",
    "rimraf": "*"
  }
}

npm install
will follow up with bower install (npm will resolve the execution path from node_modules)

npm test
will run linting and your existing test script

npm run build
will clean and re-install

I added the rimraf to remove the dependency.

Note, if you're open to pull requests, I'd be happy to do the initial changes to remove the dependencies on tools outside of node (namely the makefile, and shell script)

Test against real server

@dgraham,

While the MockXHR stuff may make the tests faster and a little simpler to run, I think we should have a real server rather than mocks.

When a real fetch implementation ships in a browser, we should be able to validate our test suite against it. And maybe find bugs in either the real or our polyfill implementation.

We could probably get a real zero dep setup if we implement a bare bones webrick handler (no rack). Other people who have no idea how Ruby works might be trying to run these tests, so they shouldn't have to deal with rubygems or bundler.

application/json content type and Chrome Canary implementation

I'm having some issues with different behaviour of this polyfill vs. Canary's implementation.

When accessing a JSON endpoint that responds with Content-Type: application/json I get the following results:

fetch('http://my/api/json-endpoint').then(resp => {
    console.log(resp);
})
// Canary output:
Response {bodyUsed: false, headers: Headers, statusText: "OK", ok: true, status: 200, type: "basic"}
// Polyfill output
Object { _bodyInit: Blob, _bodyBlob: Blob, type: "default", url: "http://taarn-net.dev/api/user/1", status: 200, statusText: "OK", headers: Object, bodyUsed: true }

I can still use resp.text(), but isn't it weird that the Canary output says no body is used and type is "basic" rather than "default"?

ie11 Promise supported?

apparenlty ie11 does not support Promises so I get this issue:

SCRIPT5009: 'Promise' is undefined
File: fetch.js, Line: 244, Column: 5

   return new Promise(function(resolve, reject) {    <=====
  var xhr = new XMLHttpRequest()
  if (self.credentials === 'cors') {
    xhr.withCredentials = true;
  }

  function responseURL() {
    if ('responseURL' in xhr) {
      return xhr.responseURL
    }

Minified distribution

Hey.

For me personally, if I'm using the fetch polyfill I'm probably going to use a task runner to package it up with others and minify it myself but wasn't sure if you envisioned having a dist folder or the likes and providing a minified version also?

@jh3y

"Failed to load response data" in chrome

I'm getting a "Failed to load response data" error in chrome. When I chrome inspect afterxhr.responseType = 'blob', xhr.responseText immediately becomes the following:

[Exception: DOMException: Failed to read the 'responseText' property from 'XMLHttpRequest': The value is only accessible if the object's 'responseType' is '' or 'text' (was 'blob').]

When I change it xhr.responseType = '' the problem goes away.

Here are the http headers:

Remote Address:127.0.0.1:3000
Request URL:http://localhost:3000/api/Orders
Request Method:GET
Status Code:200 OK

Request Headers
Accept:*/*
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-US,en;q=0.8
Cache-Control:no-cache
Connection:keep-alive
Host:localhost:3000
Origin:http://localhost:8080
Pragma:no-cache
Referer:http://localhost:8080/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36

Response Header
Access-Control-Allow-Credentials:true
Access-Control-Allow-Origin:http://localhost:8080
Connection:keep-alive
Content-Length:32
Content-Type:application/json; charset=utf-8
Date:Wed, 28 Jan 2015 04:27:25 GMT
ETag:W/"20-c4c67a6b"
Vary:Origin, Accept-Encoding
X-Powered-By:Express

There is no problem in FF. I'm using fetch via the flux Martyjs library. Could this be a problem with my API endpoint? Can anyone provide any insight into this, or reproduce it?

Let me know what I can provide to be more constructive here.

Provide example for File Download

I tried to point someone to this repo who was asking a question about an easier replacement for XHR+FileSystem for File Downloads, but there seems not be an example for this use case specifically.

Specific browser compat range

Any chance you have more detail on the browser compat range? Related: I'm curious, what is the major issue with supporting IE9?

Add support for file protocol (File URI Scheme)

Hi there,
I was working on an Android project and using webview on some pages, and I was trying to use fetch to fetch some cached data on the device, which is able to access via file:/// protocol. It seems fetch would throw a TypeError in this case for the moment.

Authorization: 'Bearer XXX' is not sent

I don't know if I am making this correct, my code looks like this:

var token = '123456789';
var requestHeaders = {
    'Authorization': 'Bearer ' + token
};
fetch(url, {
    method: 'get',
    headers: requestHeaders
})
.then(function(response) {
    console.log('hooray');
})
.catch(function(e){
    console.log('Error:', e);
});

But when inspecting the request on Firefox or Chrome Develeoper Tools, the Authorization header doesn't look like it was sent.

Timeout/End-user abort

How would I implement a timeout similar to jQuery's $.ajax timeout option? The fetch standard mentions an "end-user abort", but I don't see any detail on how it would be triggered.

Do not reject promises with non-Errors

The readme example of a jQuery-esque adapter contains

return Promise.reject(response)

This is bad practice and should not be encouraged (even though jQuery does it). Instead, it should reject with an instance of Error.

Make module available without polyfill

It would be nice to be able to require fetch without it being appended to window in a similar way to the es6-promise polyfill. If fetch can be assigned to a variable, then this means that when a broken or otherwise incompatible version of the fetch API become available in a browser, code that uses fetch won't break.

In other words:

// This fetch can be used as is.
var fetch = require('fetch');

// Do this only if you want to polyfill fetch on the window object.
fetch.polyfill();

I realise however that this breaks the existing API.

self is not defined

I have an isomorphic react app. So while this the code is not running on node, still babel complains that "self is not defined". A quick hack that makes things work is to add "self = this;" to fetch.

Would be really nice to have a proper fix for this.

Update Bower Package

The package available on Bower currently was updated 3 months ago, but since then there have been some really useful additions. Specifically for me, the addition of 'ok' to remove the need to check the status every time is a great time and code-size saving.

Would it be possible to update the Bower package to include some of the new commits?

Travis

Hey, its open source already. Lets get travis running.

Streams

They're coming.

I think @domenic has the pieces of a Stream polyfill around somewhere. We'll have to decide if we should make that an external dependency or just bundle the minimal stream implementation we need here. I think we mainly need the readable stream stuff, so maybe its not too much code. I dunno.

Also, all test coverage around this. This will probably help the FF folks check there upcoming implementation as well.

/cc @dgraham @jakearchibald @domenic

Better error when trying to `.json` a `Resource` that's not JSON.

It would be nice if there were a better error when trying to .json a Resource that's not JSON.

Currently in two implementations (this polyfill and Chrome Service Workers) trying to fetch and .json() a non JSON resource throws a not particularly helpful error:-

fetch("//some.thing/not-a-json-resource")
  .then(function(res) {
    return res.json();
  })
  .catch(function(err) {
    console.log(err); // => Unexpected token c
  });

screen shot 2014-12-24 at 12 36 59

It would make for more less unpleasant debugging / error aggregation if it threw an error along the lines of “Failed to parse {{url}} as JSON”… or something like that…

I know errors aren't spec'd as such so it's not something that should/could be fixed in the spec — but I will raise as an issue in Chrome as well so that GitHub & Chrome's fetches can stay aligned but I think it's something that should be improved and I'm sure you have an idea of what it might look like…

credentials:"cors" not in spec

dictionary RequestInit {
  // ...
  RequestMode mode;
  RequestCredentials credentials;
};

enum RequestMode { "same-origin", "no-cors", "cors" };
enum RequestCredentials { "omit", "same-origin", "include" };

You can use { mode: "cors" } or { credentials: "include" } but not { credentials: "cors" }. I got confused why my code was not working because I was coding against the spec but then looked through the fetch.js source and saw that it is not compliant ;)

Chrome leaves a tmp file on every request fetch does

Each time we do a request with fetch Chrome creates a tmp file in %LOCALAPPDATA%\Temp on Win8 x64. End never seems to remove that file after that. With this, the temp folder is full pretty soon.

We tried it on different machines with different configuration, with and without debug mode on and its allays the same.
The tmp files containing the full content of our request.

Example request

    return config.get()
      .then(()=>fetch(`${config.api}/v1/account`,{headers:{Authorization:this.token}}))
      .then(res=>res.json());

config.get returns a promise which ensures the config is loaded before we can do any request.

The requested api uses cors headers.

Any idea?

Publish to npm?

Because npm is the closest thing we have to a standard module repository for JavaScript (including the client-side). It's the largest module registry of its kind for any language. There are about 1/5th as many Bower modules as npm modules.

If that's not convincing enough: http://browserify.org/articles.html

fetch(request) fails

var request = new Request('/hello');
fetch(request);

…results in fetching /[object%20Object]

Can a fetch be aborted by the initiator?

The spec says:

A response can have an associated termination reason which is one of end-user abort, fatal, and timeout.

By "end-user" I presume it means the user who's browsing the web page that's using fetch (possibly by hitting Esc or closing the page?). But what interests me is: can the request be aborted from the same code that initiated it, for the purpose of e.g. aborting a request when it's being replaced with a newer request as a result of repetitive user interaction such as keystrokes in an input field?

If not, what are our options for discarding the outcome of a fetch that we're not interested in anymore? Thanks

/cc @annevk

npm-ignore files

You can use files in package.json to specify the limited subset of project files that should be published to npm.

upload progress?

Is there any way I can attach a progress listener to the xhr.upload object used in fetch?

xhr.upload.addEventListener('progress', function(){ ... })

since I use fetch to upload files, I would find this very useful.

Review Chrome Canary fetch() interop

Chrome Canary has a native fetch() exposed with the experimental web feature flag on. It's still a limited implementation, but we should review it against our test suite and see what's busted then make sure there's Chrome bug tickets tracking those issues.

Some known issues already

  • Doesn't support redirects #42
  • Doesn't send origin cookies #43

Run test suite under Worker contexts

It'd be great to run all the same document tests under a worker context as well.

QUnit is a bit of a let down for making this happen. I'd be open to other test runners assuming its simpler overall.

Testing Service Worker context may be interesting too. I'm not sure how many differences there would be from a generic web worker context. fetch is at least enabled by default for SW when it might not be for a regular worker.

/cc @dgraham @matthew-andrews @domenic @knowledgecode

IE8 and IE9 support

What is the reason this does not work in IE8 and IE9? Could it be made so a shim or polyfill is all that is needed for older browsers to work?

Interceptors?

Is there the concept of interceptors in fetch? I'd like to add a header to all of my requests automatically.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.