Joshua's Cheatsheets - JS Networking Requests - Cheatsheet
Light
help

For some handy snippets, check out the "Network stuff" section of my JS Snippets page.

Fetch

Fetch is a newer browser/web API, that is promise based, and can replace the use of the XMLHttpRequest api.

Fetch Resources

What & Link Type
MDN Docs Doc
MDN Guide ("Using Fetch") Guide

Fetch Usage

Signature:

/**
* @returns {Promise} that resolves to `Response` object
*/
fetch(resource, options);

Simple demo (from MDN):

fetch('http://example.com/movies.json')
	.then(function(response) {
		return response.json();
	})
	.then(function(myJsonObj) {
		console.log(JSON.stringify(myJsonObj));
	});

// Async version
const res = await fetch('http://example.com/movies.json');
const jsonObj = await res.json();
console.log(jsonObj);

What about advanced usage? Options?

Options should be passed as second parameter. Full list is in detail here, and also listed below:

___ ___ ___ ___
method headers body mode
credentials cache redirect referrer
referrerPolicy integrity keepalive signal

Response members/methods:

Type Key
Property headers
Property ok
Property redirected
Property status
Property statusText
Property type
Property url
Property useFinalUrl
Property body
Property bodyUsed
Method clone()
Method error()
Method redirect()
Method arrayBuffer()
Method blob()
Method formData()
Method json()
Method text()

Fetch - GET with Query String Parameters

Sadly, fetch does not have built-in support for passing GET parameters as a raw JS object. However, thanks to the URL() Web API and URLSearchParams API, constructing a URL string with query params is pretty easy:

const params = {
	userId: 2,
	referrer: 'cheatsheets'
};
const endpoint = 'https://jsonplaceholder.typicode.com/posts';

// Use URL() instance to append query string automatically & format
const url = new URL(endpoint);
url.search = new URLSearchParams(params);

// Notice how we just use URL directly with fetch, but it is NOT a string; it is a URL instance. Fetch will *automatically* call `.toString()` on non-string first arguments
// Better practice might be to explicitly use `url.toString()`
const jsonObj = (await (await fetch(url)).json());
console.log(jsonObj);

Note: Using the URL() API has a lot of benefits, aside from easier construction. It also handles a lot of issues around encoding / escaping, so you don't even have to think about that when passing stuff in.

Fetch - Sending POST body

Use FormData:

var formData = new FormData();
formData.append('email', 'foo@example.com');
fetch('https://example.com/checkEmail', {
	method: 'POST',
	body: formData
});

The FormData constructor can also take an HTML <form> element in the constructor, to grab the data out of, but you can't pass in a raw object. It is pretty easy to write a helper function to fill the data though:

const objToFormData = (obj) => {
	const formData = new FormData();
	Object.keys(obj).forEach(key => formData.append(key, obj[key]));
	return formData;
}

Or, if sending as JSON payload, make sure it is stringified!

fetch('https://example.com/checkEmail', {
	method: 'POST',
	headers: {
		'Content-Type': 'application/json'
	},
	body: JSON.stringify({
		email: 'foo@example.com'
	})
});

Fetch - Using Headers

There are multiple ways to send custom headers with a fetch request.

Simple object key-pairs

fetch('https://example.com/postJson', {
	method: 'POST',
	headers: {
		'Content-Type': 'application/json'
	},
	body: myJsonPayload
});

Explicit constructor

const myHeaders = new Headers();
myHeaders.append('Content-Type', 'application/json');
fetch('https://example.com/postJson', {
	method: 'POST',
	headers: myHeaders,
	body: myJsonPayload
});

Or, pass object directly to constructor:

const myHeaders = new Headers({
	'Content-Type': 'application/json'
});
fetch('https://example.com/postJson', {
	method: 'POST',
	headers: myHeaders,
	body: myJsonPayload
});

Why use the Headers() interface? There are few benefits; reusability, handy methods, validity checker, etc. Read more here.

Fetch - CORS

As expected, the fetch() API is subject to the same CORS restrictions that the other browser APIs are. Mainly that if you try to make a cross-origin request, and the response does not have the proper CORS headers, the request will fail.

However, fetch has a specific option for when you want to make a CORS request but don't care about the response. An example of when this might be useful is pinging a server (e.g. with POST) or sending a tracking hit; you might not need to know what the server responds with. This option is setting mode to no-cors, which is also known as using an opaque response.

The response is opaque, because you cannot read its contents.

fetch('http://google.com',{
	mode: 'no-cors'
}).then(function(response){
	console.log(response.type);
	// logs > "opaque"
	return response.text();
}).then(function(t){
	console.log("text = " + t);
	// logs > "text = "
});

Warning: "no-cors" has no impact on CORB and can still be blocked by it.

Fetch - Credentials

Like jQuery's withCredentials option, fetch has init options for passing cookies / credentials. See docs for details, but basically the credentials param in the init config object needs to be a value from an enum - one of these options:

  • omit
  • same-origin
  • include

There are also experimental plans to allow for more advanced types of credentials; again, refer to the docs for details.


XMLHttpRequest

@TODO

XMLHttpRequest - Resources

What & Link Type
MDN Docs Doc
MDN Guide Guide

JSONP Cross-Origin / Cross-Domain Workaround

JSONP (JSON with Padding) is now considered a legacy approach to working around cross-origin issues; CORs and mechanisms that support it are the preferred current solution. JSONP still has some value though, as some sites still refuse to return the correct headers to support CORs, but accept JSONP responses.

How Does JSONP Work?

JSONP, at its core, is very simple and usually is comprised of two major parts.

  1. The main concept is that instead of fetching JSON data via XMLHttp or fetch(), you use a <script src="..."></script> tag.
  2. Since loading via script tag can lead to an undetermined state ("is my data there yet or not?"), the second part of JSONP is telling the responding server to call a specific callback function in the window scope. This is also necessary when the response would normally be a pure JSON object, since those don't become part of the global scope anyways.

    • This is usually done by query string. E.g.: <script src="//example.com/data/?callback=myCallback"></script>
    • When the server responds, it calls your callback with the data payload: myCallback(dataObj). Padding the JSON with the callback code is what makes the P (Padding) part of JSONP.

JSONP - Pseudo Code Example

Client:

<!-- Define Callback -->
<script>
window.myCallback = (data) => {
	console.log('JSONP response received', data);
}
</script>
<script src="//example.com/data/?callback=myCallback"></script>

example.com/data Server Endpoint (Pseudo Code):

app.get('/data', (req, res) => {
	const callbackName = req.query.callback;
	const data = {color: 'red'};
	// Wrap in callback padding: `callback(data)`
	res.send(`${callbackName}(${JSON.stringify(data)})`);
	// Or, use shortcut:
	// res.jsonp(data);
});

JSONP - Important Reminders

There are a few important things to remember with JSONP:

  • It is up to the responding server to implement JSONP on their end.

    • Just because you call a JSON endpoint with a callback= query parameter doesn't mean that you will get back a JSONP response. It is not the browser that implements JSONP, it is the server!
  • JSONP has some major security concerns, another reason to prefer newer alternatives

JSONP - Example Client-Side Code

const fetchViaJsonp = (url, callback) => {
	const callbackName = 'jsonp_callback_' + Math.round(100000 * Math.random());
	window[callbackName] = function (data) {
		delete window[callbackName];
		document.body.removeChild(script);
		callback(data);
	};

	const script = document.createElement('script');
	script.src = url + (url.indexOf('?') >= 0 ? '&' : '?') + 'callback=' + callbackName;
	document.body.appendChild(script);
};

You could easily adapt the above code to return a Promise, to be similar to fetch.

Markdown Source Last Updated:
Wed Jul 01 2020 07:56:19 GMT+0000 (Coordinated Universal Time)
Markdown Source Created:
Wed Aug 21 2019 00:46:11 GMT+0000 (Coordinated Universal Time)
© 2020 Joshua Tzucker, Built with Gatsby