Recipe 10.7

Exporting API Data With a Download Link

If you don’t want to worry about file permissions, or if you need wider browser support, you can convert the data to a Blob and create an object URL, which you can then set as a link’s href attribute. This will download the data like a normal file download from the browser.

Demo

Export
First name Last name Department
Loading...
Loading Users

Code

JavaScript
const exportLink = document.querySelector('#export-link');

let userList;

function loadUsers() {
  // Make the request
  return fetch('/api/users')
    // Parse the response body as an object
    .then(response => response.json())
    // Handle errors, including network and JSON parsing errors
    .catch(error => console.error('Couldn\'t fetch:', error.message));
}

loadUsers().then(data => {
  userList = data;
  document.querySelector('#loader').remove();
  renderUsers(userList);

  // Clean up the previous export data, if it exists.
  const currentUrl = exportLink.href;
  if (currentUrl) {
    URL.revokeObjectURL(currentUrl);
  }

  // Need a Blob for creating an object URL
  const blob = new Blob([JSON.stringify(userList, null, 2)], {
    type: 'application/json'
  });

  // The object URL links to the Blob contents - set this in the link
  const url = URL.createObjectURL(blob);
  exportLink.href = url;
  exportLink.classList.remove('d-none');
});

/**
 * Renders an array of users in the user table.
 * @param userList the array of users
 */
function renderUsers(userList) {
  const tableBody = document.querySelector('#users tbody');
  userList.forEach(user => {
    renderUser(user, tableBody);
  });
}

/**
 * Renders a user object as a row in the user table.
 * @param user the user object to render
 * @param tableBody the table body to append the row to
 */
function renderUser(user, tableBody) {
  const row = document.createElement('tr');
  
  const firstName = document.createElement('td');
  firstName.textContent = user.firstName;
  row.appendChild(firstName);

  const lastName = document.createElement('td');
  lastName.textContent = user.lastName;
  row.appendChild(lastName);

  const department = document.createElement('td');
  department.textContent = user.department;
  row.appendChild(department);

  tableBody.appendChild(row);
}
HTML
<a class="btn btn-primary d-none" id="export-link" download="users.json">
  <i class="bi bi-box-arrow-down"></i> Export
</a>
<table id="users" class="table">
  <thead>
    <tr>
      <th class="w-25">First name</th>
      <th class="w-25">Last name</th>
      <th class="w-25">Department</th>
    </tr>
  </thead>
  <tbody>
    <tr id="loader">
      <td colspan="3" class="text-center p-4">
          <div class="spinner-border" role="status">
            <span class="visually-hidden">Loading...</span>
          </div>
          <div>Loading Users</div>
      </td>
    </tr>
  </tbody>
</table>
Web API Cookbook
Joe Attardi