Fetch Requests

Fetch requests are a way to send HTTP requests to a server directly from a page using JavaScript. The advantage of using fetch requests over, say, reloading the page are that

  1. The contents of the page do not need to be reloaded each time the request is made

  2. The request takes a little less time to get processed compared to sending server-to-server requests from the backend.

  3. Allows content to be loaded "optionally" - content is not loaded on the page until it is specifically requested by the user, so pages have less content and are rendered faster.

Syntax

Fetch requests follow this syntax:

fetch(resource, options)
  • resource is the server (or resource) that the request is being sent to. It is a string with the URL.

  • options contain some custom settings for the request. More details later

They return a "Promise" - in other words, they return an asynchronous wrapper for the response from the server.

For those of you who have taken CS2030/S, Promises in JavaScript are like CompletableFutures in Java.

This promise can be processed using the .then method, which takes in a unary function. This function is applied on the response recieved from the server.

fetch(resource, options)
.then(processResponse);

The response

The response from a fetch request is a JSON object with parameters like status and protocol and body. Of these, the status and body parameters are the most useful for now.

response.status is an integer representing the HTTP Response Code returned by the server. The full list of response code can be found here, but a few common ones are below:

  • 200 OK - all good

  • 404 NOT FOUND - the url/resource was not found on the server side

  • 500 Internal Server Error - the server ran into an error when trying to process the request

response.body is an encoded object that contains the body of the response. Note that failed requests have null as the value for the body property for their response objects. To decode the body into readable JSON that can be processed by your JS code, use the method response.json().

You could imagine using the status property in this way:

fetch("somedomain.com/some-route/", someData)
.then(response => {
    if (response.status === 200) {
        let data = response.json(); // get the data
        // do something with the data
    } else {
        // show the user some error message
        alert(`Error: ${response.status}`);
    }
})

This status-checking and body-retrieval is often chained into 2 different promises using two sequential thens:

fetch("somedomain.com/some-route/", someData)
.then(response => response.status === 200 ? response.json() : response.status)
.then(data => {
    if (typeof(data) === "number") {
        // show the user some error message
        alert(`Error: ${response.status}`);
    } else {
        // process the data
    }
})

Let's walk through it step by step:

  1. fetch("somedomain.com/some-route/", someData) - this line performs the fetch request and returns the response wrapped in a promise

  2. .then(response => response.status === 200 ? response.json() : response.status) - here, we check the response status code to see if the request was successful.

    • If the response was successful (i.e. response.status === 200) then we return the body of the response using response.json()

    • If the response was not successful, we return the status code of the response so it can be shown to the user

  3. .then(data => {...}) - this section of the code takes the data returned after processing the response

    • If the data returned is a number (i.e. typeof(data) === "number") then this means that the response status code was returned by the previous function, so there was an error, so we tell the user as such

    • Otherwise process the data as normal

Note that you may want to add more intermediary conditions and process different response codes differently, but the general form of a fetch request is as above.

The options

Note that the fetch function takes in two parameters: the resource, and the options.

options is a JSON object that contains any custom settings that you may want to apply to the request. This includes header data, type of content being sent (if any), the content itself, and others. If any settings are ommitted, then the browser plugs in the default values for each setting. Below is an example of the options object, filled with default values for each setting (not all possible settings are shown).

let options = {
    method: "GET",
    mode: "cors",
    cache: "default",
    credentials: "same-origin",
    redirect: "follow",
    headers: {
        "Content-Type": "application/json",
        "Access-Control-Allow-Origin": "*"
    },
    body: JSON.stringify(someJSONData)
}

Let's look at each setting:

  1. method - this setting specifies the request method. There are 4 commonly used methods(GET, POST, PUT and DELETE), and each are used in different situations. More details here

  2. mode - what resource sharing mode should be allowed. "cors" stands for "Cross-Origin Resource Sharing", and this mode allows sharing resources across origins. The other options are "no-cors" and "same-origin". More details here

  3. cache - how the request interacts with the browser's cache. Some different options are "no-cache", "no-store", "reload", and "only-if-cached". These options are explained here

  4. credentials - this specifies whether or not the user should send/receive cookies from the resource. The two other options are "omit" (never send/receive cookies) and "include" (always send/receive cookies). More details here

  5. redirect - this specifies how to handle if the resource redirects our request elsewhere. Some other options are "error" and "manual". More details here

  6. headers - this is a JSON object that contains the headers for the request. The sample shows two header settings:

    • "Content-Type" - this header specifies the type of content being requested. It is in the format "type/subtype" and some examples are "image/png" (a png image), "text/plain" (plain text), or "multipart/form-data" (multiple parts of form data). The full list of options can be found here.

    • "Access-Control-Allow-Origin" - this header indicates whether the response can be shared with requesting code from the given origin. This could be "*", so all origins can have access to the response, or "<origin>" where <origin> is a domain/IP address. More details here

  • There are many more headers that can be assigned, and they are all listed here.

  1. body - this setting specifies the body of the request (i.e. some data that needs to be processed by the resource server). Note that this must be a string; JSON.stringify allows us to represent a JSON object with the request body as a string. Note also that a body CANNOT be present for a GET request.

The Mozilla Developer Network web docs are a very very useful resource for understanding HTTP requests and responses, as well as frontend web development with JavaScript.

Next steps

Next, we'll write our own fetch request to the NUSMods API.

Last updated