Published on

Attempts at Tutorials - JavaScript API Request via Fetch

Authors

So as a fun way to come up with more blog posts(or filler if your a pessimist) I thought trying to write a few basic tutorials might work. So for this exciting first episode I'll be covering how to make an API request, I'll be using the NASA Photo of the Day as the example since I think they won't complain.

So to begin with go to the NASA site here here and have a look around. Also get yourself an API key rather then using mine. The query we'll be using is this https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY where DEMO_KEY will be our own API key's respectively. We'll be using Fetch to make the request.

Very Basic Version

To see the page using this code go here. The whole thing is a functional React component which Next uses as a page.

So to begin with we need to get the full response to our request, so

const response = await fetch('https://api.nasa.gov/planetary/apod?api_key=')

We're using const since we're not actually changing the response object once we've got it so no need to use var or let. Plus my VS Code complained when I used var. We're using await since we want to get the response before we continue since we can't return an image and it's easier to have it be linear.

Next step is to get the JSON from the response object.

const photo = await response.json()

As long as everything worked and remembered to pay your internet bill you should get a JSON object like this once

{
  "date": "2023-09-19",
  "explanation": "Do stars always create jets as they form? No one is sure. As a gas cloud gravitationally contracts, it forms a disk that can spin too fast to continue contracting into a protostar.  Theorists hypothesize that this spin can be reduced by expelling jets. This speculation coincides with known Herbig-Haro (HH) objects, young stellar objects seen to emit jets -- sometimes in spectacular fashion. Pictured is Herbig-Haro 211, a young star in formation recently imaged by the Webb Space Telescope (JWST) in infrared light and in great detail.  Along with the two narrow beams of particles, red shock waves can be seen as the outflows impact existing interstellar gas. The jets of HH 221 will likely change shape as they brighten and fade over the next 100,000 years, as research into the details of star formation continues.",
  "hdurl": "https://apod.nasa.gov/apod/image/2309/HH211_webb_3846.jpg",
  "media_type": "image",
  "service_version": "v1",
  "title": "HH 211: Jets from a Forming Star",
  "url": "https://apod.nasa.gov/apod/image/2309/HH211_webb_960.jpg"
}

For now we're going to ignore how to take advantage of TypeScript. For the page we need three parts of it:

  • Title
  • Explanation
  • Url

The Title will be used at the top of the page for the heading. The Explanation will be used for text underneath the image, and as the img tag alt text. The Url is also for the img tag to be the src attribute..

If we want a simple area to display it in we can use some HTML like below. The {} is how we're adding the variable parts to the HTML.

<div>
  <p className="text-3xl text-center" style={{ marginBottom: '2.5rem' }}>
    {photoData.title}
  </p>
  <img src={photoData.url} style={{ marginBottom: '2.5rem' }}></img>
  <p>{photoData.explanation}</p>
</div>

The whole code is below. The useState and useEffect are part of React. I'm using useState so we can have a loading message to begin with, using an alert from DaisyUI and replace it with the real content once the API call is done. The useEffect then lets the component update. Inside the useEffect is the async function called getPhoto with the setPhotoArea with the information from the API. The [] at the end of the useEffect is so we don't call the NASA API continuously and exceed the amount of API calls that can be used. Something I didn't forget to do while making the page to begin with and get Error 429 Too Many Requests after a short while. The PageSEO on this and the advanced version are for google, and for the tab in your browser. I've included it so show the whole page component.

import { PageSEO } from '@/components/SEO'
import { useEffect, useState } from 'react'

const SimpleNasaPhoto = () => {
  const [photoArea, setPhotoArea] = useState(
    <div className="alert alert-info">
      <svg
        xmlns="http://www.w3.org/2000/svg"
        fill="none"
        viewBox="0 0 24 24"
        className="stroke-current shrink-0 w-6 h-6"
      >
        <path
          strokeLinecap="round"
          strokeLinejoin="round"
          strokeWidth="2"
          d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
        ></path>
      </svg>
      <span>Now loading, please wait.</span>
    </div>
  )

  useEffect(() => {
    async function getPhoto() {
      const response = await fetch(
        'https://api.nasa.gov/planetary/apod?api_key=r2wfZ5FK629lmMf2OJCphFaNr6Eip88SE9s2eyId'
      )

      const photoData = await response.json()

      setPhotoArea(
        <div>
          <p className="text-3xl text-center" style={{ marginBottom: '2.5rem' }}>
            {photoData.title}
          </p>
          <img src={photoData.url} style={{ marginBottom: '2.5rem' }}></img>
          <p>{photoData.explanation}</p>
        </div>
      )
    }

    getPhoto()
  }, [])

  return (
    <>
      <PageSEO
        title="Simple Nasa Photo of the Day via Fetch API"
        description="The example page using the Fetch API to get the NASA Photo of the day. See blog tutorial for more details."
      />
      <div>{photoArea}</div>
    </>
  )
}

export default SimpleNasaPhoto

Advanced Version Or Maybe Over Engineered

So what could we do to make it more reliable? Putting aside it being the NASA API so the odds of it breaking soon I think are slim let's pretend we want to check the response before we do something on the page. After all if we get a 500 or 400 error we don't have anything to display. To see the page itself click here.

Before we did this:

const response = await fetch('https://api.nasa.gov/planetary/apod?api_key=')

const photoData = await response.json()

So if the API calls fails somehow, NASA says I've requested too many times, or other errors the photoData won't be what we want. Rather then worrying too much about checking for individual error codes, we're just going to check if it's 200 which means the request worked, and if not it'll go to the else. The else will be a simple alert showing the response.status and the response.statusText so we can see what went wrong. Normally you don't want to show the end user the error and instead just state something happened or redirect to a shared error page but that doesn't help with the tutorial. Though in theory beyond reading the source of the page here or the dev tools on the page it should all work nicely.

import { PageSEO } from '@/components/SEO'
import { useEffect, useState } from 'react'

const AdvancedNasaPhoto = () => {
  const [photoArea, setPhotoArea] = useState(
    <div className="alert alert-info">
      <svg
        xmlns="http://www.w3.org/2000/svg"
        fill="none"
        viewBox="0 0 24 24"
        className="stroke-current shrink-0 w-6 h-6"
      >
        <path
          strokeLinecap="round"
          strokeLinejoin="round"
          strokeWidth="2"
          d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
        ></path>
      </svg>
      <span>Now loading, please wait.</span>
    </div>
  )

  useEffect(() => {
    async function getPhoto() {
      const response = await fetch(
        'https://api.nasa.gov/planetary/apod?api_key=r2wfZ5FK629lmMf2OJCphFaNr6Eip88SE9s2eyId'
      )

      console.log(response.status)

      if (response.status == 200) {
        const photoData = await response.json()
        setPhotoArea(
          <div>
            <p className="text-3xl text-center" style={{ marginBottom: '2.5rem' }}>
              {photoData.title}
            </p>
            <img src={photoData.url} style={{ marginBottom: '2.5rem' }}></img>
            <p>{photoData.explanation}</p>
          </div>
        )
      } else {
        setPhotoArea(
          <div className="alert alert-info">
            <svg
              xmlns="http://www.w3.org/2000/svg"
              fill="none"
              viewBox="0 0 24 24"
              className="stroke-current shrink-0 w-6 h-6"
            >
              <path
                strokeLinecap="round"
                strokeLinejoin="round"
                strokeWidth="2"
                d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
              ></path>
            </svg>
            <span>
              The catch all response for this page. Something went wrong. The error code was
              {response.status} and the text is {response.statusText}
            </span>
          </div>
        )
      }
    }

    getPhoto()
  }, [])

  return (
    <>
      <PageSEO
        title="Advanced Nasa Photo of the Day via Fetch API"
        description="The advanced example page using the Fetch API to get the NASA Photo of the day. See blog tutorial for more details."
      />
      <div>{photoArea}</div>
    </>
  )
}

export default AdvancedNasaPhoto

What about TypeScript?

If we wanted to use TypeScript we could take the JSON from the 200 response and define it as a type or interface. The Ifor theinterfaceversion is so when seeing it in the code later we know what it is. Using either can be useful for more complex examples so if we were to try and do something that might break or cause problems when the code is running, it instead can warn us in the code editor. I've not made a page though since as far as I know the browser doesn't use TypeScript and it gets converted to JavaScript so there would be nothing to see in the inspector. To get an idea of the difference between type andinterface click here to go to a Log Rocket blog post on the subject.

type NasaPhoto = {
  date: string;
  explanation: string;
  hdurl: string;
  media_type: string;
  service_version: string;
  title: string;
  url: string;
}

interface INasaPhoto = {
  date: string;
  explanation: string;
  hdurl: string;
  media_type: string;
  service_version: string;
  title: string;
  url: string;
}

Lesson Over

Hopefully this has been useful or at least not made understanding how to use Fetch more difficult. This of course only looked at GET there are other methods an API call can use, a main one being POST to send something to the server. That might be the topic of another tutorial. Another way to call an API is with XMLHttpRequest which I plan on making a tutorial for.

  • Mozilla Fetch Documentation here
  • Mozilla Using the Fetch API Documentation here and probably better then this tutorial