JanBulling

Spotify-API

Show what you are listening to

Create a Spotify Application

First, we need to create a Spotify application to give us credentials to authenticate with the API. The Spotify Web-API is free to use, but you need to have a spotify premium account to access all functionalities.

All done! You now have a properly configured Spotify application and the correct credentials to make requests.

Get Credentials For Authentication

There are a view different ways to authenticate with the Spotify API. We will use the Authentication Code Flow.

First, we need an authorizatiion code, where we need to specify, what information about we will get access to. Make a GET request to the following endpoint:

https://accounts.spotify.com/authorize?client_id=<your_id>&response_type=code&redirect_uri=http://localhost:3000&scope=user-read-currently-playing user-modify-playback-state

For the redirect URI you can put any url that you have registered at the spoify dashboard in step 1. As the scope, you can put any scope you will need for you project to work. Here, we just need ‘user-read-currently-playing’.

After authorizing, you’ll be redirected back to your redirect_uri. In the URL, there’s a code query parameter. Save this value.

http://localhost:3000/callback?code=NApCCg..BkWtQ

Next, you need to retrieve the refresh token. You need to make a POST request like this.

curl -H 'Authorization: Basic <base64 encoded client_id:client_secret>' -d grant_type=authorization_code -d code=<code> -d redirect_uri=http://localhost:3000 https://accounts.spotify.com/api/token

You can e.g. make this request with javascript like this:

const getRefreshToken = await fetch('https://accounts.spotify.com/api/token', {
    method: "POST",
    headers: {
        Authorization: `Basic ${Buffer.from(`${client_id}:${client_secret}`).toString('base64')}`,
        'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: new URLSearchParams({
        grant_type: 'authorization_code',
        code: <code from the previous step>,
        redirect_uri: "http://localhost:3000"
    })
});
console.log(await getRefreshToken.json());

This will return a JSON response containing a refresh_token.

Add Environment Variables

To securely store the tokens and keys for the API, we create an .env.local file at the root of the project and store the client_id, client_secret and the refresh_token there. You can access the environment variables in code like this: process.env.<ENV_NAME>

Code

const basic = Buffer.from(`${<client_id>}:${<client_secret>}`).toString('base64');
const TOKEN_ENDPOINT = `https://accounts.spotify.com/api/token`;

const getAccessToken = async () => {
    const response = await fetch(TOKEN_ENDPOINT, {
        method: "POST",
        headers: {
            Authorization: `Basic ${basic}`,
            'Content-Type': 'application/x-www-form-urlencoded'
        },
        body: new URLSearchParams({
            grant_type: 'refresh_token',
            refresh_token: <refresh_token>
        })
    })

    return response.json();
}

With this access token, we have access to the Spotify Web-API. We can e.g. get the song, the user of the client_id is listening to:

const NOW_PLAYING_ENDPOINT = `https://api.spotify.com/v1/me/player/currently-playing`;

export const getNowPlaying = async () => {
  const { access_token } = await getAccessToken();

  return fetch(NOW_PLAYING_ENDPOINT, {
    method: "GET",
    headers: {
      Authorization: `Bearer ${access_token}`,
      "Content-Type": "application/json",
    },
  });
};

This method can be called by e.g. a next.js api function like this:

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const response = await getNowPlaying();

  // 204: No data available, >= 400: error
  if (response.status === 204 || response.status > 400) {
    return res.status(200).json({ isPlaying: false });
  }

  const song = await response.json();

  if (song.item === null) {
    return res.status(200).json({ isPlaying: false });
  }

  const title = song.item.name;
  const artist = song.item.artists
    .map((_artist: any) => _artist.name)
    .join(", ");

  return res.status(200).json({
    artist,
    title,
  });
}