Fetch

  • Basic GET request getting HTML or text
  • Basic GET returning JSON
  • POST request
  • POST form content with FormData
  • POST request serialise FormData
  • POST request serialise form to JSON (for minimal API request handlers)
  • Uploading data as JSON
  • Uploading a file
  • Uploading multiple files
  • Downloading a file with POST
  • Download a file with GET
  • Cascading/Dependent dropdown

Content Type

The default content type for a fetch request using the POST method depends on the type of data you provide in the request body:

  • FormData object: multipart/form-data
  • URLSearchParams object: application/x-www-form-urlencoded
  • Text string: text/plain;charset=UTF-8
  • Blob or BufferSource: The browser will attempt to determine the content type based on the internal data (e.g., image/jpeg for a JPEG image).
  • JavaScript object: No default content type is set. You'll need to explicitly set it to application/json if you intend to send JSON data.

Here's a summary of the default content types:

Request Body Type Default Content Type
FormData multipart/form-data
URLSearchParams application/x-www-form-urlencoded
String text/plain;charset=UTF-8
Blob or BufferSource Determined based on data
JavaScript object None (must be explicitly set)

Remember:

  • Always explicitly set the Content-Type header if you want to ensure a specific content type, even if the default matches your intention.
  • To send JSON data, explicitly set the Content-Type header to application/json.
  • To send form data, use a FormData object and the default content type will be handled for you.

Basic GET request getting HTML or text

document.getElementById('load').addEventListener('click', () => {
    fetch('/ajax')
        .then((response) => {
            // call the text() method on the response, which returns the actual text from the ReadableStream
            return response.text(); 
        })
        // result represents the result of calling the text() method
        .then((result) => {
           document.getElementById('grid').innerHTML = result;
        });
});

document.querySelector('#load').addEventListener('click', _ => {
    fetch('/ajax')
        .then(response => response.text())
        .then(result => document.querySelector('#grid').innerHTML = result);
});

Basic GET returning json

document.getElementById('load').addEventListener('click', () => {
    fetch('/ajax')
        .then((response) => {
            // call the json() method on the response, which parses json from the ReadableStream
            return response.json(); 
        })
        // result represents the result of calling the text() method
        .then((json) => {
           document.getElementById('grid').innerHTML = json;
        });
});

POST request

fetch(url, {
    method: 'post',
    headers: {
      // important to set the content type - see above
      "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
    },
    body: 'foo=bar&lorem=ipsum'
  })
  .then(response => response.json())
  .then(data =>{
    console.log('Request succeeded with JSON response', data);
  })
  .catch(function (error) {
    console.log('Request failed', error);
  });

POST form content with FormData

let data = new FormData();
data.append('foo', 'bar');
data.append('lorem','ipsum');
fetch(url, {
    method: 'post',
    headers: {
      // important to set the content type
      "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
    },
    body: new URLSearchParams(data).toString()
  })
  .then(response => response.json())
  .then(data =>{
    console.log('Request succeeded with JSON response', data);
  })
  .catch(function (error) {
    console.log('Request failed', error);
  });

POST request serialise form

fetch(url, {
    method: 'post',
    headers: {
      // important to set the content type
      "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
    },
    body: new URLSearchParams(new FormData(theFormElement)).toString()
  })
  .then(response => response.json())
  .then(data => {
    console.log('Request succeeded with JSON response', data);
  })
  .catch(function (error) {
    console.log('Request failed', error);
  });

POST request serialise form to JSON (for minimal API request handlers)

Note: this needs the ts compiler options set to target at least 2019

fetch(url, {
    method: 'post',
    headers: {
      // important to set the content type to application/json
      "content-type": "application/json"
    },
    body: JSON.stringify(Object.fromEntries(new FormData(theFormElement)))
  })
  .then(response => response.json())
  .then(data => {
    console.log('Request succeeded with JSON response', data);
  })
  .catch(function (error) {
    console.log('Request failed', error);
  });

Uploading JSON data

Use fetch() to POST JSON-encoded data.

const data = { username: 'example' };

fetch('https://example.com/profile', {
  method: 'POST', // or 'PUT'
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(data),
})
.then(response => response.json())
.then(data => {
  console.log('Success:', data);
})
.catch((error) => {
  console.error('Error:', error);
});

Uploading a file

Files can be uploaded using an HTML input element, FormData() and fetch().

const formData = new FormData();
const fileField = document.querySelector('input[type="file"]');

formData.append('username', 'abc123');
formData.append('avatar', fileField.files[0]);

fetch('https://example.com/profile/avatar', {
  method: 'post',
  body: formData
})
.then(response => response.json())
.then(result => {
  console.log('Success:', result);
})
.catch(error => {
  console.error('Error:', error);
});

Uploading multiple files

Files can be uploaded using an HTML input element, FormData() and fetch(). Ensure that the enctype of the form is set to multipart/form-data

const formData = new FormData();
const photos = document.querySelector('input[type="file"][multiple]');

formData.append('title', 'My Vegas Vacation');
for (let i = 0; i < photos.files.length; i++) {
  formData.append(`photos_${i}`, photos.files[i]);
}

fetch('https://example.com/posts', {
  method: 'POST',
  body: formData,
})
.then(response => response.json())
.then(result => {
  console.log('Success:', result);
})
.catch(error => {
  console.error('Error:', error);
});

Downloading a file with POST

printButton.addEventListener('click', e => {
    let formData = new FormData();
    formData.append('productName', selectedProductName);
    formData.append('issue', selectedIssueName);
    [...document.querySelectorAll('[name="entries"]:checked')].forEach(x => formData.append('selectedItems', x.value));
    [...document.querySelectorAll('[name="columnoptions"]:checked')].forEach(x => formData.append('selectedColumns', x.value));
    let fileName = `Running List - ${selectedProductName} ${selectedIssueName}`;
    let options = {
        method: 'POST',
        body: new URLSearchParams(formData).toString(),
        headers: {
            'content-type': 'application/x-www-form-urlencoded'
        }
    }
    fetch('/editorialentry/runninglist', options)
        .then(res => res.blob())
        .then(blob => {
            let a = document.createElement('a');
            a.href = window.URL.createObjectURL(blob);
            a.download = fileName;
            a.click();
        });
})

Cascading/Dependent dropdown

Call loadIssues on change in principal selector. Use replaceChildren in dependent selector

const loadIssues = (id) => {
    let dependentSelector = document.querySelector('#CommissionedForId');
    fetch(`/api/getdata?id=${id}`).then(response => response.json()).then(items => {
        let newOptions = [new Option('', '')]; // blank option first
        items.forEach(item => {
            newOptions.push(new Option(item.Text, item.Value));
        });
        dependentSelector.replaceChildren(...newOptions);
    });
}

Download with GET

let fileName = '';
    const allItems = [...invoiceItems].map(x => x.value);
    const selectedItems = [...invoiceItems].filter(checkbox => checkbox.checked).map(x => x.value);
    let qs = selectedItems.length ? selectedItems.join('&ids=') : allItems.join('&ids=');
    fetch(`/invoicing?handler=invoicerequests${qs}`).then(response => {
        const header = response.headers.get('Content-Disposition');
        const parts = header.split(';');
        fileName = parts[1].split('=')[1].replaceAll("\"", "");
        return response.blob();
    }).then(blob => URL.createObjectURL(blob))
    .then(href => {
        Object.assign(document.createElement('a'), {
            href,
            download: fileName,
        }).click();
        URL.revokeObjectURL(href);
    })