How to Make a React TypeScript App that Connects to an API

How to Make a React TypeScript App that Connects to an API

Step-by-Step Walkthrough on How to Make a Simple App with a 3rd Party API call using React and TypeScript

ยท

6 min read

Ok first off apologizes for not posting for a bit. I've been slammed!

Today I'm going to teach you how to make a React TypeScript App that connects to a simple third party API.

If you're new to APIs, then you're in the right place. This post is for beginners.

Introducing the Cute Cat Pic API app

I'm going to start by posting a simple React app that grabs a random cute cat pic using thecatapi.com.

Don't get overwhelmed by the below code (made from create-react-app template). We're going to walk through it step by step!

import { useEffect, useState } from 'react';
import './App.css';
import axios, { AxiosResponse } from 'axios';

interface CatTributes {
  id: string;
  url: string;
}

const CAT_URL = 'https://api.thecatapi.com/v1';

function App() {
  const [cats, setCats] = useState<CatTributes>({ id: '', url: '' });
  const [loading, setLoading] = useState<boolean>(false);
  const [error, setError] = useState<string>('');

  // get random cat image from API
  const getCats = async () => {
    // loading state begins
    setLoading(true);
    setError('');

    // grab random cat photo
    const response: void | AxiosResponse = await axios
      .get(`${CAT_URL}/images/search`)
      .catch((err) => {
        // set error state
        setError(err.message);
        setLoading(false);
        console.error(err);
      });

    // loading done
    setLoading(false);

    if (!response) {
      setError('no cat images found! sorry!');
      return;
    }

    // extracting data
    const { data } = response;
    const [{ url, id }] = data;
    console.log({ url, id });

    // setting state
    setCats({ url, id });
  };

  // load random cat image on load
  useEffect(() => {
    getCats();
  }, []);

  // return jsx showing cat picture and image id
  return (
    <div className='App'>
      {loading && <p>Loading...</p>}
      {error && <p>{error}</p>}
      {cats.id !== '' && <div>
        <img 
            style={{ height: '50vmin' }} 
            src={cats.url} 
            alt='cute cat photo'
        />
        <p>image ID: {cats.id}</p>
      </div>
      }

    </div>
  );
}

export default App;

Let first take a peak of the first part of the app (skipping the imports and interface declaration):

const CAT_URL = 'https://api.thecatapi.com/v1';

function App() {
  const [cats, setCats] = useState<CatTributes>({ id: '', url: '' });
  const [loading, setLoading] = useState<boolean>(false);
  const [error, setError] = useState<string>('');

CAT_URL is just the url provided by the wonderful people at thecatapi.com that we can use to make a GET request to their server to get a random cat pic back.

๐Ÿ˜บ

A GET request is where you're simply querying the 3rd party server and requesting a response back, usually in the form of data that you can show to your user.

We'll get into how we use a get request in a second. First off, let me explain our state here. It consists of 3 variables:

  • cats: holds our current cat pic and cat id
  • loading: this is true when the asynchronous API call is in progress. Typically pretty fast.
  • error: when the cat has a hissy fit and no cat pic is returned! If there's an error connecting to the server, we'll update this state with an error message.

Now let's take a look at the actual function that will make the API call to grab out cat pics.

  // get random cat image from API
  const getCats = async () => {
    // loading state begins
    setLoading(true);
    setError('');

    // grab random cat photo
    const response: void | AxiosResponse = await axios
      .get(`${CAT_URL}/images/search`)
      .catch((err) => {
        // set error state
        setError(err.message);
        setLoading(false);
        console.error(err);
      });

First, you'll probably notice async in front of our getCats function and if you look closely, there an await in front of axios.

async/await allow us to make an asynchronous call that forces the javaScript engine to wait until the call completes and returns a result, instead of leaving us with a Promise we have to resolve! ๐Ÿ˜…

With async/await you can avoid those crazy nested then/catch statements you may have seen before.

Why Axios?

You'll also notice I'm using an external library called axios, instead of fetch (JavaScript built-in function for making API requests).

Why axios? Well, there's 2 big reason: it has some nice features that make it a bit easier to work with, and it has wide browser support. It will work on some older browsers where fetch may have issues with.

That being said, if you're taking a coding exam for a job interview and cannot import axios for the api calls, it's best you know how to use fetch as well!

Coding tests where they provide an API and ask you to make a simple app, that performs a GET and/or POST request to the API, and print out some API data to the screen, are extremely common.

Make sure to Catch any Errors

Back to the code...if things don't go as planned, we have a catch statement that grabs the error message that is return. We then save that error message to state using setError(err.message)

Let's look at the getCats function:

    // loading done
    setLoading(false);

    if (!response) {
      setError('no cat images found! sorry!');
      return;
    }

    // extracting data
    const { data } = response;
    const [{ url, id }] = data;
    console.log({ url, id });

    // setting state
    setCats({ url, id });
  };

First, we're now done with the API call so we can setLoading(false). Notice we also do that in the catch statement right above that.

More Error Checking...

Second, we're checking to make sure we have a valid response: if (!response) {. Ya, we checked for errors with .catch((err) => { ...} at the end of our axios call. However, this only checks for server errors.

We could have a no server errors and potentially an empty response.

Let's Extract the Data...Finally!

Finally, we can extract data and set the state. With REST APIs the response usually comes back as an object with a data field.

So we can destructure data from response.

Now data is typically the data you requested in the form of an array of objects.

In this case the data array contains just 1 items, that is an object with 2 fields: url and id.

As you can guess, url is the adorable cat pic we've been waiting for ๐Ÿˆ, and id is the unique id associated with that pic.

This line: const [{ url, id }] = data; may look intimidating. We're just doing a double destructuring here! First we're pulling the first element from data array using [] and since the element is an object we can destructure it with {url, id}.

๐Ÿค“

Now it's time to display the data we received from the server...

  // load random cat image on load
  useEffect(() => {
    getCats();
  }, []);

  // return jsx showing cat picture and image id
  return (
    <div className='App'>
      {loading && <p>Loading...</p>}
      {error && <p>{error}</p>}
      {cats.id !== '' && <div>
        <img 
            style={{ height: '50vmin' }} 
            src={cats.url} 
            alt='cute cat photo'
        />
        <p>image ID: {cats.id}</p>
      </div>
      }

    </div>
  );
}

The useEffect runs when the application first mounts and calls getCats() to load up our random cute cat pic from theCatAPI.

Rendering the Output

Finally, we have a return statement where we return JSX to either: show loading..., an error message, or our adorable cat pic ๐Ÿ˜ป.

Using the {...&&...} operator is a quick and easy way to render the correct output we based on the current state.

Homework Assignment

Best way to learn is by doing, so here a quick assignment...

I encourage you to copy this code and do the following:

  1. Add a button that calls getCats() on click.
  2. Refactor the code so that instead of having 3 useState's we can clean up the code, and tie it all together with 1 useReducer hook.
  3. Break up the JSX that's returned into multiple child components. Maybe have separate components for each state: Loading, Error, and rendering the cat image?
  4. There's a small bug in the code above. Can you find it?

Did you find this article valuable?

Support Amit Mehta by becoming a sponsor. Any amount is appreciated!

ย