React + custom hook + typescript to download a file through API

Wondering how to download files on UI by calling an API behind authentication?
anubhav goel
Anubhav Goel

October 3, 2021 | 5 minutes read

Problem Statement

  • File needs to be downloaded at the frontend.
  • Data of that file is being served by the backend.

Possible Solutions

1. Embed the link of the file to be downloaded on tag, and download the file in the new tab.

< a href="https://www.some/url/to/file" target="__blank"> Download < / a>

The above approach works fine for simpler use cases when the file being served is public and can be accessed by the whole world.

But what if the file is behind some kind authentication and can only be called via API by providing necessary authentication details (like bearer token in request authorization header).

This will result in error when downloading the file, and you’ll find the backend error shown on the UI.

error-handling

Error while downloading file since authentication details were not provided

2. Call the API

To overcome the issue faced in approach one, we’ll have to access the file by calling the API. In the API request we can additionally provide necessary details to make the backend application aware that we have access to the requested file.

This is the approach we are going to discuss, i.e., call the API, get the data download this data as a file on the browser.

Approach

  1. On click of the button, call the download API for the file which needs to be downloaded as a BLOB.
  2. Create url out of the blob and store the object downloaded in the browser memory.
  3. Click the hidden anchor tag programatically which links to the above generated url.

Technology used

  1. React + Typescript: for single page application
  2. Axios: for API calls.
  3. react-bootstrap + bootstrap: for ready to use components and styling.
  4. luxon: to deal with dates.

Application setup

  1. Create a react + typescript boilerplate as mentioned in official react documentation (https://create-react-app.dev/docs/adding-typescript/)

npx create-react-app react-download-file-axios –template typescript

2. Install helper libraries

npm i axios @types/axios luxon @types/react bootstrap react-bootstrap

Let’s start coding

Button component

A simple reusable component for a button which can handle state for loading when calling the API.

Accepts three props:
  1. buttonState: Button can be in loading or a normal state (primary).
  2. onClick: on click handler function which needs to be called on click of button.
  3. label: text shown on the button

Reusable custom hook: useDownloadFile.ts

Purpose of this code is to abstract the download file functionality into a reusable custom hook so that it can be reused across many pages. Always better to follow the famous Don’t Repeat Yourself (DRY) programming principle.

src/customHooks/useDownloadFile.ts

Let’s go through this function now.

DownloadFileProps

The hook accepts various parameters:

  1. apiDefinition
    An axios API called wrapped inside a function which contains API definition.
  2. preDownloading
    Function which is executed just before calling the API.
    This function can be used as a pre hook to do any tasks which needs to be done before the API call is made.
    Example: Disable button / change button state to loading.
  3. postDownloading
    Function which is executed after making the API call.
    This function can be used as a post hook to do any tasks which needs to be done after the API call is made.
    Example: Enable button / change button state to primary.
  4. onError
    Function to be called when API has resulted in failure.
    Error handling can be done over here.
    Example: Show an alert to the user saying something has went wrong and the file is not downloaded.
Utilities exposed by the hook
  1.  ref: ref which will be attached to the <a /> tag in the parent component.
  2. url: URL representing the data fetched from the API.
  3. name: name of the file which the user sees when downloading it on their system.
  4. download: function which needs to be invoked when file needs to be downloaded.
Flow of download function:
  1. Invoke preDownloading function.
  2. Call the API. In case of any error, onError function will be invoked to handle the error.
  3. Create a url which represents the downloaded data and store it in the state. This url is provided to <a href... />.
  4. Generate the name of the file by which it will be downloaded in the browser. Store the same name in the state.
  5.  Click the <a /> in the DOM to download the file.
  6. Invoke postDownloading function.
  7. Destroy the generated url.
Component to download the file: downloadSampleCsvFile
Finally let us stitch in all the pieces of code to download a sample csv file on click of a button.

src/components/downloadSampleCsvFile/index.tsx

This file is pretty straightforward.

There are two state variables to maintain state of the button, and show alert in case of error while calling API.

To the useDownloadHook custom hook various functions are passed:

  1. preDownloadFile: sets button state to loading.
  2. postDownloadFile: sets button state back to primary.
  3. onError: shows something went wrong alert.
  4. apiDefinition: axios get call to the API with responseType of blob type.
  5. getFileName: generates the file name based on current time.

Then the component returns the JSX:

  1. < a /> : hidden tag not visible to the user. Function download internally clicks this button after file is downloaded using refs.
  2. < button /> : button which the user clicks to download the required file.
react-download-button

Different Button States

3. < Alert /> : responsible for showing error message on the UI

alert-message

Alert when calling API results in error.

App.tsx file

Finally integrate <DownloadSampleCsv /> component to <App />

npm start / yarn start

  1. Custom hook can be enhanced to support more functionalities.
  2. Github repo can be found on https://github.com/anubhav-goel/react-download-file-axios.