01/02/21

Using the Spotify API

The album of the week at the bottom of my website is actually one of my Spotify playlists. So the data is not pulled from the CMS, but through the Spotify API. You can query all sorts of things from the API: artists, songs, playlists. You can even build a more extensive app with a search function.

POST

All requests to the API require authentication with OAuth. "Open Authorization" is a standard protocol that enables secure API authorization. To achieve this we'll need to receive a valid OAuth access token. To get this token, you'll first need to register your project in the Spotify Dashboard. There you'll get a Client ID and a Secret Key. Second, we need to make a request to the Spotify server with these credentials and "ask" for a token.

I'm using a simple Gatsby Site for this example. Make sure to save your Client ID and Secret Key in a .env file. Since this is sensitive data you don't want it to be tracked in your Git repo.

GATSBY_SPOTIFY_CLIENT_ID=123
GATSBY_SPOTIFY_SECRET=123

API calls must always happen in the useEffect hook!

import React, { useEffect, useState } from 'react';

const DJ = () => {
  
  useEffect(() => {
    // API call
  }, []);

  return <div />;
};

export default DJ;

A fundamental concept of the Web is a protocol that enables the communication between client and server. It's called Hypertext Transfer Protocol (HTTP). Various request methods are available for this purpose. Two of them, POST and GET, are the most commonly used. To connect to the Spotify server to get the token, we first need to make a POST request. POST means sending data to a server.

So we ask the server: "Here are my credentials. Please give me the token!"

We use the Fetch API for this, which is part of all modern browsers. With Fetch, you can make asynchronous requests to different servers. It takes two arguments. fetch(requestURL, options)

First, you specify the URL where the request should go to. The second argument contains the request method (POST). The "body" is another important parameter, as this is where the actual data we want to send is packaged. In this case, we'll send a string that contains the credentials. You can access the credentials via the .env file with process.env.KEY. The data in the body is sent in a specific format. Therefore it's necessary to specify this in "headers". The header has the function to send additional meta information.

The Fetch method returns a response object. The response object contains a body in which the data we need is stored. But we can't access it directly from the response, that's why we have to convert it to JSON first.

import React, { useEffect, useState } from 'react';

const DJ = () => {
  
  useEffect(() => {
    const fetchData = async () => {
      const authResponse = await fetch(
        'https://accounts.spotify.com/api/token',
        {
          method: 'POST',
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
          },
          body: `grant_type=client_credentials&client_id=${process.env.GATSBY_SPOTIFY_CLIENT_ID}&client_secret=${process.env.GATSBY_SPOTIFY_SECRET}`,
        }
      );
      const authData = await authResponse.json();
    };
    
    fetchData().catch((error) => console.error(error));
  }, []);

  return <div />;
};

export default DJ;

GET

If all goes well, we get back our access token, which we need to access the actual data we want.

For this, we need to communicate with the server again. This time, however, we want to GET data. Therefore we'll make another request. I am requesting the data of a playlist in this example, but there are other endpoints as well. Make sure to set the playlist to public, otherwise the API call won't work.

In the second argument of the function we need to specify some options again. We make a GET method, authenticate with the token and expect a JSON format. "Bearer" is the token type.

Now we get the playlist data back! Finally, with React, you can store the data with useState.

import React, { useEffect, useState } from 'react';

const DJ = () => {
  
  useEffect(() => {
    const [data, setData] = useState(null);
      
    const fetchData = async () => {
      const authResponse = await fetch(
        'https://accounts.spotify.com/api/token',
        {
          method: 'POST',
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
          },
          body: `grant_type=client_credentials&client_id=${process.env.GATSBY_SPOTIFY_CLIENT_ID}&client_secret=${process.env.GATSBY_SPOTIFY_SECRET}`,
        }
      );
      const authData = await authResponse.json();
      
      const playlistResponse = await fetch(
        'https://api.spotify.com/v1/playlists/{playlist_id}',
        {
          method: 'GET',
          headers: {
            Authorization: `Bearer ${authData.access_token}`,
            'Content-Type': 'application/json',
          },
        }
      const playlistData = await playlistResponse.json();
      
      setData(playlistData);
    };
  
    fetchData().catch((error) => console.error(error));
  }, []);

  return <div />;
};

export default DJ;

Just console.log the data and see which ones you want to use. For example artist and album:

import React, { useEffect, useState } from 'react';

const DJ = () => {
  
  useEffect(() => {
    const [data, setData] = useState(null);
    
    const artist = data?.tracks.items[0].track.artists[0].name;
    const album = data?.tracks.items[0].track.album.name;
      
    const fetchData = async () => {
      const authResponse = await fetch(
        'https://accounts.spotify.com/api/token',
        {
          method: 'POST',
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
          },
          body: `grant_type=client_credentials&client_id=${process.env.GATSBY_SPOTIFY_CLIENT_ID}&client_secret=${process.env.GATSBY_SPOTIFY_SECRET}`,
        }
      );
      const authData = await authResponse.json();
      
      const playlistResponse = await fetch(
        'https://api.spotify.com/v1/playlists/{playlist_id}',
        {
          method: 'GET',
          headers: {
            Authorization: `Bearer ${authData.access_token}`,
            'Content-Type': 'application/json',
          },
        }
      const playlistData = await playlistResponse.json();
      
      setData(playlistData);
    };
  
    fetchData().catch((error) => console.error(error));
  }, []);

  return (
    <div>
      <p>{artist}</p>
      <p>{album}</p>
    </div>
  );
};

export default DJ;