APIs, AJAX, and Axios#

To download the demo code for this lecture, run:

$ dmget wb-apis-ajax --demo

Then, cd into the project directory. Install the dependencies and run the app:

$ npm install
$ npm run dev

To load the webpage, go to http://localhost:8000/ in your browser.

APIs#

  • API = Application Programming Interface

  • A way of getting data from the Internet

  • Examples:

    • Yelp API provides info on restaurants in a particular area

    • iTunes API provides info about songs given an artist, title, etc

JSON#

  • Normally, web responses return an HTML page. We don’t want a whole page, we just want some data.

  • JSON (JavaScript Object Notation) is the most common format for sending/receiving data in the browser

    {
        "title": "Hey Jude",
        "artist": "The Beatles",
        "year": 1968,
        "composers": ["John Lennon", "Paul McCartney"]
    }
    
  • JSON is a string! (All web responses must be strings.)

  • JSON provides a standard format to communicate regardless of language

    • In JavaScript, it can be converted into an object or an array

    • Most other languages have similar data structures, so JSON can be translated into those languages too

  • Many, many APIs use JSON

Using APIs#

Look for documentation! Here’s what you can expect:

  • List available endpoints (i.e. routes)

    • Endpoints are appended to the base URL of the API, just like a route.

  • Tell you how to format request parameters

  • Tell you what you’ll get back

  • Explain rate limits, authorization requirements, etc

Example: Spotify API Documentation

REST#

  • REST = Representational State Transfer

  • A set of standards for sending/receiving data using HTTP

  • It means that a request or response will require/include ALL the data

    • Each request is independent of each other

    • No information can be saved from one request to the next

REST is not the only standard for APIs. Another one is GraphQL. But REST is the most common, especially for public APIs.

Inspecting API Output#

Often you can just paste the URL directly into your browser and see the output.

Chrome has a JSON Viewer extension that you can use to format the JSON in a more readable way.

PokeAPI output

You can also use the curl command to get the JSON and output it to a file:

$ curl "https://itunes.apple.com/search?term=mountain" > mountain.json

You can then import the JSON file. Often the data structure has several layers of nesting, so it can be helpful to explore it one layer at a time.

wb-apis-ajax-demo/json-demo/itunes_json.js#
import mountainSongs from './mountain.json' assert { type: "json" };

console.log(Object.keys(mountainSongs));  // ['resultCount', 'results']
console.log(mountainSongs['results'][0]); // big object ...

const firstSong = mountainSongs['results'][0];
console.log(Object.keys(firstSong));  // 'trackName', 'artistName', etc

// Getting a track name out of the data structure
console.log(mountainSongs['results'][0]['trackName']);

AJAX#

AJAX is a technique for sending requests and receiving responses from a server without having to reload the browser page.

AJAX Requests

Why Use AJAX?#

  • Don’t need to reload the entire page if just 1 thing is changing

  • Allows you to only fetch data when that data is necessary (helpful for page load times)

  • Makes websites interactive

  • Useful for calling APIs

  • Core Job Skill

Making AJAX Requests in JavaScript#

import axios from 'axios';

axios.get('/some-url')
  .then((response) => {
    document.querySelector('#my-div').innerText = response.data;
  });

We can use the axios library to make network requests to a server.

Here, we are using the axios.get method to make a HTTP GET request to the URL /some-url.

Asynchronous Code#

HTTP requests can take a while though!

  • We need to wait for a response to come back from the server.

  • But we don’t want to stop the rest of the program while we’re waiting.

  • Axios needs to wait for a response from the server, so it returns a Promise

  • A Promise says, “I promise I’ll run this function as soon as this asynchronous operation is done”

  • Any code after that will still run immediately

Asynchronous Laundry#

  • When you put your clothes in the washing machine, you don’t wait around by the washer until it is done running.

  • Instead, you do something else (like work on a JavaScript project)

  • But you promise to come back and take the clothes out when they’re done (especially if you share the washer with others)

    axios.get('/put-clothes-in-washer')
      .then(removeClothesFromWasher) // removeClothesFromWasher is a callback function
    
    workOnJavaScriptProject(); // this runs BEFORE removeClothesFromWasher!
    

.then()#

  • The function that is passed to the .then() will run when the asynchronous code is done running

  • Anything that depends on the data we are getting should happen in the function you pass to the .then() call

Axios Example#

star-wars.js#
const episodeNum = Math.ceil(Math.random() * 6); // random number 1-6

axios.get(`https://swapi.dev/api/films/${episodeNum}`)
  .then(res => {
    console.log('this will be logged second');
    document.querySelector('#star-wars-movie').innerText = res.data.title;
  });
console.log('this will be logged first');

Here, we are using Axios to call the Star Wars API and display a random Star Wars movie title.

Sending Data with Axios#

GET Requests#

In a GET request, data is sent using the query string (part of the URL). Here we’re asking for a specific number of Pokemon and displaying them on the page:

pokemon.js#
function getPokemon() {
  const numPokemon = document.querySelector('#num-pokemon').value;
  const url = `https://pokeapi.co/api/v2/pokemon?limit=${numPokemon}`;

  axios.get(url).then(response => {
    let pokemonList = "";
    for (const pokemon of response.data.results) {
      pokemonList += `<li>${pokemon.name}</li>`;
    }
    document.querySelector('#pokemon-list').innerHTML = pokemonList;
  });
}

const button = document.querySelector('#get-pokemon');
button.addEventListener('click', getPokemon);

Longer Query Strings#

If you have a longer query string, JavaScript can help you construct it

const queryString = new URLSearchParams({ offset: 200, limit: 10}).toString();

const url = `https://pokeapi.co/api/v2/pokemon?${queryString}`;

Async/Await#

  • Instead of using .then(), there is another syntax you can use to work with promises, called async/await.

  • The keyword await can be used with Axios calls (or anything else that returns a promise)

  • await makes JavaScript wait until that promise settles and returns its result. So code execution actually pauses!

  • If using await inside a function, the function must be marked with async – otherwise it won’t work.

    async function f() {
      const pokemon = await axios.get('https://pokeapi.co/api/v2/pokemon');
      console.log(pokemon.data.results);
    }
    

async before a function means that a function always returns a promise. If an async function has some other type of return value, that value is automatically wrapped in a resolved promise.

Top Level Await#

  • In modern browsers, await can also be used on the top level (i.e. not inside any functions).

  • In this case you don’t need async.

    const pokemon = await axios.get('https://pokeapi.co/api/v2/pokemon');
    console.log(pokemon.data.results);
    

Full Example with Async/Await#

pokemon-async-await.js#
async function getPokemon() {
  const numPokemon = document.querySelector('#num-pokemon').value;
  const url = `https://pokeapi.co/api/v2/pokemon?limit=${numPokemon}`;

  const response = await axios.get(url);

  let pokemonList = "";
  for (const pokemon of response.data.results) {
    pokemonList += `<li>${pokemon.name}</li>`;
  }
  document.querySelector('#pokemon-list').innerHTML = pokemonList;
}

const button = document.querySelector('#get-pokemon');
button.addEventListener('click', getPokemon);

POST Requests#

In a POST request, you can’t use a query string – data is sent in the request body.

Pass the request body into axios.post as a second argument, after the URL:

axios.post('/order-cookies', {
  type: 'Chocolate Chip',
  qty: 4
})

Axios isn’t just for calling APIs – you can also call an Express route. Just make sure the route returns JSON (or text), rather than rendering an HTML template.

Axios POST Request: Demo#

order-cookies.js#
async function orderCookies(evt) {
  evt.preventDefault();  // prevent 'submit' event from reloading the page

  const formData = {
    cookieType: document.querySelector("#cookie-type-field").value,
    qty: document.querySelector("#qty-field").value
  }

  const response = await axios.post('/order-cookies', formData);
  document.querySelector('#order-status').innerText = response.data.msg;
  document.querySelector('#order-total').innerText = "Total: $" + response.data.total.toFixed(2);
}

document.querySelector('#order-form').addEventListener('submit', orderCookies);

The /order-cookies route works just like a regular POST route in Express, except use res.json() to send JSON:

app.js#
app.post('/order-cookies', (req, res) => {
  const qty = Number(req.body.qty);
  const cookieType = req.body.cookieType;
  if (Number.isNaN(qty) || qty < 1) {
    res.json({ msg: 'Invalid Order', total: 0 });
  } else {
    const unitPrice = qty > 6 ? 1.8 : 2.0;
    res.json({
      msg: `Your order of ${qty} ${cookieType} cookies is confirmed`,
      total: unitPrice * qty,
    });
  }
});

Axios Error Handling#

Sometimes, Axios requests will not succeed. You can use try and catch to handle errors.

For example, say you wanted to alert users that their request to order cookies was not successful:

try {
  const response = await axios.post('/order-cookies', formData);
  document.querySelector('#order-status').innerText = response.data.msg;
  document.querySelector('#order-total').innerText = "Total: $" + response.data.total.toFixed(2);
}
catch (err) {
  alert(err.message)
}