The global fetch
function is an easier way to make web requests and handle responses than using an XMLHttpRequest.
This implementation uses GM_xmlhttpRequest as the underlying implementation, allowing user scripts to make cross-domain
requests using the fetch API.
GM_fetch.ts differs from upstream GM_fetch by being rewritten in TypeScript and featuring a more consistent API. GM_fetch is based on GitHub's fetch polyfill.
- GM_fetch.ts uses the legacy
GM_xmlhttpRequest
API as opposed to GreaseMonkey 4.0'sGM.xmlHttpRequest
for compatibility with a wider variety of userscript engines. As such, while it is compatible with TamperMonkey, ViolentMonkey, Scriptish, and legacy GreaseMonkey, it is unlikely to work on GreaseMonkey 4.0. - The default UTF-8 TextDecoder encoding is used for decoding response ArrayBuffer instances, with the exception of
Response.text()
. -
Response.text()
is untested. -
Body.formData()
is unsupported for response bodies. -
request.credentials: 'include'
isn't explicitly implemented.
For regular userscripts, without any type of module bundler, you can add an @require
clause to your script's header
pointing to dist/GM_fetch.js
. This is a UMD file that will adapt to either RequireJS, AMD, or Webpack, or export a
GM_fetch variable to the current scope. The Headers, Request, and Response classes are only publicly visible if a module
loader is used.
// @grant GM_xmlhttpRequest
// @require https://raw.githubusercontent.com/uwx/GM_fetch.ts/master/dist/GM_fetch.js
Note: This always links to the most up-to-date version, which may introduce breaking changes. Pinning to a commit, or self-hosting, is recommended.
Alternatively, for TypeScript projects, you can copy all the files in src
to your project directly, and import
./GM_fetch.ts
.
The fetch
function supports the GET, HEAD and POST HTTP methods, as with GM_xmlhttpRequest. Here are some examples.
fetch('/users.html')
.then(res => res.text())
.then(body => document.body.innerHTML = body);
fetch('/users.json')
.then(res => res.json())
.then(json => console.log('parsed json', json)
.catch(err => console.error('parsing failed', err);
fetch('/users.json').then(response => {
console.log(response.headers.get('Content-Type'));
console.log(response.headers.get('Date'));
console.log(response.status);
console.log(response.statusText);
});
var form = document.querySelector('form')
fetch('/query', {
method: 'post',
body: new FormData(form)
});
fetch('/users', {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Hubot',
login: 'hubot',
})
});
const input = document.querySelector('input[type="file"]')
const form = new FormData()
form.append('file', input.files[0])
form.append('user', 'hubot')
fetch('/avatars', {
method: 'post',
body: form
});
This causes fetch
to behave like jQuery's $.ajax
by rejecting the Promise
on HTTP failure status codes like 404, 500, etc. The response Promise
is
resolved only on successful, 200 level, status codes.
function status(response) {
if (response.ok) {
return response;
}
throw new Error(response.statusText);
}
fetch('/users')
.then(status)
.then(res => res.json())
.then(json => console.log('request succeeded with json response', json))
.catch(err => console.log('request failed', err));