Docs

Everything You Need to Know

Fetch

Use

Ajax has served us well over the years, so why fetch? Well, let's take a look at the differences. A plain JavaScript XMLHttpRequest could look like this:

var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'json';

xhr.onload = function() {
  console.log(xhr.response);
};

xhr.onerror = function() {
  console.log("Booo");
};

xhr.send();

And here's the same thing using jQuery, which gives us more organization:

$.ajax({
  url: './some/url',
  type: 'GET',
  dataType: 'json',
  success: function(data) {
    console.log(data);
  },
  error: function(err) {
    console.log(err);
  }
});

Now here's the same thing using the new fetch API:

fetch('./some/url')
  .then(function(response) {
    return response.json();
  })
  .then(function(data) {
    console.log(data);
  })
  .catch(function(err){
    console.log(err);
  });

As you can see in the above example, fetch uses promises to its advantage and the result is a much cleaner and easier to follow process. Of course, you could use jQuery deferred objects, or convert jQuery promises into ES6 promises. But fetch's advantage is that promises are built in. It's how fetch works.

API

Fetch takes two arguments: input and init. Input is a url. If only a url is provided, fetch assumes a GET request. Init is an optional object of key/value pairs. The possible values for init are:

init: {
  method: {
    'GET',
    'POST',
    'PUT',
    'DELETE',
    'OPTIONS',
    'HEAD'
  },
  headers: {
    'Accept': 'application/json',
    'Accept-Encoding', 'deflate',
    'Accept-Encoding', 'gzip'
    'Content-Type': 'application/json',
    "Content-Type": "application/x-www-form-urlencoded",
    'Content-Type', 'image/jpeg',
    'Content-Type', 'text/html'
  },
  body: {
    json,
    text,
    formData,
    blob,
    arrayBuffer
  },
  mode:  {
    "cors",
    "no-cors",
    "same-origin",
  },
  credentials: {
    "omit",
    "same-origin",
    "include"
  },
  cache: {
    "default",
    "no-store",
    "reload",
    "no-cache",
    "force-cache",
    "only-if-cached"
  },
  timeout: 10000
}

OK, so how do you use this thing? Here are some examples:

In this example we fetch from a url, parse the JSON, pass it to a forEach function and append the data to the document.

$("#submit").on("click", function(e) {
  fetch('../data/wines.json')
  // Parse response to JSON:
  .then($.json)
  .then(function(json) {
    json.forEach(function(wine) {
      $('#message_ajax').append('<li>' + wine.name + '</li>');
    })
  });
});

If you want to get some an HTML document or document fragment, do the following:

fetch('https://foo.com/fragments/page-1.html')
  .then(function(response) {
    return resonse.text();
  })
  .then(function(result) {
    $('body').append(result);
  });

$.json

This method automatically parses data returned by the response as JSON. To use it, simply put it as the immediate response to a JSON request:

.then($.json)

See the example above. You can use this whenever you expect JSON will be returned.

Here we are posting some form data that will get processed by a PHP controller. We provide a url, followed by an init object that contains the 'post' method, headers object and the body, which holds the data from the serialized form:

$("#submit").on("click", function(e) {
  // Serialize form data:
  var data = $.serialize($('form')[0]);
  fetch('../controllers/php-post.php', {
    method: 'post',
    headers: {  
      "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"  
    },
    body: data
  })
  .then($.json) 
  .then(function(data) {
    if(data.email_check == "valid"){
        $("#message_ajax").html("<div class='successMessage'>" + data.email + " is a valid e-mail address. Thank you, " + data.name + ".</div>");
        $("#message_ajax").append('<p>' + data.msg + '</p>');
    } else {
        $("#message_ajax").html("<div class='errorMessage'>Sorry " + data.name + ", " + data.email + " is NOT a valid e-mail address. Try again.</div>");
    }
  });
});

In the above example, our PHP controller processes form data and then returns some information which we can output to the document when it was successful.

Put

To perform a PUT operation, we provide the url, followed by an init object with the 'put' method, headers and a body holding the form data that we want to put. The PHP controller at the other end of the url will handle this.

$("#submit").on("click", function(e) {
  var data = $('#fileText').val();
  fetch('../controllers/php-put.php', {
    method: 'put',
    headers: {  
      "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"  
    },
    body: data
  })
  .then($.json) 
  .then(function(data) {
      console.dir(data.base);
      $("#message_ajax").append('<p>' + data.result + '</p>');
      $("#message_ajax").append('<p>The file name is: ' + data.fileName + '</p>');
  })
  .catch(function(error) {
      console.log(error);
      $("#message_ajax").html("<div class='errorMessage'>Sorry, put was not successful.</div>");
  });
});

If this put request was handled successfully by the server side controller, it will return some data that we will output to the document.

Delete

This shows how to implement a delete. In this case we are simply deleting a file on the server. If the file does not exist, our PHP controller will let us know, otherwise it will delete the file.

The delete request needs the url to send to, followed by an init object with the 'delete' method, a headers object and the body to hold the name of the file to delete.

$("#submit").on("click", function(e) {
  var file = $('#fileName').val();
  fetch('../controllers/php-delete.php', {
    method: 'delete',
    headers: {  
      "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"  
    },
    body: file
  })
  .then($.json) 
  .then(function(data) {
      $("#message_ajax").html("<div>DELETE was sent to the server successfully.</div>");
      $("#message_ajax").append('<p>' + data.result + '</p>');
  },
  function(data) {
    console.log('PROBLEM')
    console.log(data);
  })
  .catch(function(error) {
      $("#message_ajax").html("<div class='errorMessage'>Sorry, 'DELETE' was not successful.</div>");
      error.reject();
  });
});

If the server side controller was successful, it will send back some information that we can output to the document.

Headers

In fetch you can create headers using the Headers method:

fetch('some/particular/url', {
  method: 'post',
  headers: {
    "Content-Type": "text/plain",
    "Content-Length": content.length.toString(),
    "X-Custom-Header": "ProcessThisImmediately"
  }
});

// or:
var headers = new Headers({
  "Content-Type": "text/plain",
  "Content-Length": content.length.toString(),
  "X-Custom-Header": "ProcessThisImmediately",
});
fetch('some/particular/url', {
  method: 'post',
  headers: headers
});

// or:
var reqHeaders = new Headers();
reqHeaders.append("Content-Type", "text/plain"
reqHeaders.append("Content-Length", content.length.toString());
reqHeaders.append("X-Custom-Header", "ProcessThisImmediately");
fetch('some/particular/url', {
  method: 'post',
  headers: headers
});

With the headers object, you can choose the style that works for the situation.

Request Object

You can also create a Request object to use with fetch:

// Create Request Object:
var wineRequest = new Request('../data/wines.json');
$("#submit").on("click", function(e) {
  fetch(wineRequest)
  // Parse response to JSON:
  .then($.json)
  .then(function(json) {
    json.forEach(function(wine) {
      $('#message_ajax').append('<li>' + wine.name + '</li>');
    })
  });
});

or:

var data = $.serialize($('form')[0]);
// Create Request Object:
var postRequest = new Request('../controllers/php-post.php', {
  method: 'post',
  headers: {  
    "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"  
  },
  body: data
});
fetch(postRequest);

The Request object accepts the same arguments as the fetch method, so you can pass it a url, along with an init object with method, headers, mode and cache, or whatever you need in your case:

var myInit = { 
  method: 'GET',
  headers: myHeaders,
  mode: 'cors',
  cache: 'default' 
};
var myRequest = new Request('my/special/url', myInit);
fetch(myRequest)
.then(function(response) {
  console.log('We got something here.')
})

Sending Serialized Data

You can serialize the values of a form to send, but you need to set the header to do so:

headers: {  
  "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"  
}

Look at the previous fetch examples to see how we used this.

response.ok

You can check the status of a response by examining its ok property:

fetch('some/url')
.then(function(reponse) {
  if (response.ok) {
    console.log('Woohoo! We were successful!');
  } else {
    console.log('There seems to have been a network problem.');
  }
})

You can query any of the following values on the Response object like we did with ok:

ok
status
statusText
type
url

Timeouts

If you pass fetch a timeout value in its init object, you can cancel a request taking too long:


var data = $.serialize($('form')[0]);

fetch('../controllers/php-post.php', {
  //=====================
  // Set a timeout value:
  timeout: 50000,
  //=====================
  method: 'post',
  headers: {  
    "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"  
  },
  body: data
})
.then($.json) 
.then(function(data) {
  if(data.email_check == "valid"){
      $("#message_ajax").html("<div class='successMessage'>" + data.email + " is a valid e-mail address. Thank you, " + data.name + ".</div>");
      $("#message_ajax").append('<p>' + data.msg + '</p>');
  } else {
      $("#message_ajax").html("<div class='errorMessage'>Sorry " + data.name + ", " + data.email + " is NOT a valid e-mail address. Try again.</div>");
  }
})
// Catch the timeout:
.catch(function(error) {
  console.log(error);
  // returns: Request timed out at: /controllers/php-post.php
});

Handling HTTP Error Status

Unlike normal Ajax requests, the promise returned by fetch will not get rejected, even if the response is 404 or 500. You can get arround this by defining a custom 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
  }
}

fetch('/candies')
  .then(checkStatus)
  .then($.json)
  .then(function(data) {
    console.dir(data);
  })
  .catch(function(error) {
    console.log('Request failed: ', error);
  })

Handling Cookies

By default, fetch will not send cookies. This can result in session authentication failing. To get fetch to send cookies you need to provide the proper credentials:


fetch('/candies', {
  credentials: "same-origin"
});

Receiving Cookies

The fetch API is not allowed to read the Set-Cookie header in the response. Instead, it is the browser's responsibility to handle new cookies being set. Any new cookies set in this manner will be available from document.cookie.

JSONP

Although not a part of the fetch API, we created an interface for JSONP that matches that of fetch.

$("#submit").on("click", function(e) {
  $.jsonp('https://something/is/here?name=chipper', { timeout: 10000 })
  .then($.json)
  .then(function(obj) {
    console.log(obj)
    obj.forEach(function(repo) {
      $('#message_ajax').append("<li>" + repo.name + "</li>");
    });
  })
  .catch(function(error) {
    $('#message_ajax').append("<li>" + error.message + "</li>")
  });
});

Jsonp takes the following parameters, though only the url is required:

url,
options: {
  timeout: 5000,
  callbackName: 'myCallbackHandler',
  clear: true/false
}

The url is of course the url you wish to query. You can set a timeout value so if the request is taking too long you can cancel. The default value for callback is good for the majority of sites. However, some sites have a custom handler name they use. You can provide that using the callbackName value. Setting the clear property to false will allow the script and variable for your JSONP request to persist in the browser window. By default the value is set to false so that they are automatically removed.