Blog (NextJS) + Spotify: sounds good!

June 22, 2022 (2y ago)

An iceberg of react frameworks, showing that while most of the time it seems it's just components and business logic, the reality is that under water there are many other things frameworks are handling for you, like authentication, rendering, routing, state management, i18n, styling, and more.

A few months ago I found a really cool feature while exploring Lee Robinson's blog, VP of Developer Experience at Vercel. He connected the NextJS blog with his Spotify account and I was very curious about this integration. Fortunately, like this blog, all the code was open on Github. Well, you can freely explore the repository (and I recommend it) or for now follow this article and understand how it works.

An iceberg of react frameworks, showing that while most of the time it seems it's just components and business logic, the reality is that under water there are many other things frameworks are handling for you, like authentication, rendering, routing, state management, i18n, styling, and more.

First of all, based on simple REST principles, the Spotify Web API endpoints return JSON metadata about music artists, albums, and tracks, directly from the Spotify Data Catalogue. Using NextJS we can create a simple server-side application that accesses user related data through the Spotify Web API.

I don't want to talk about CSS or any other styling. The purpose of this article is to quickly teach you how to make this connection and get to this point:

An iceberg of react frameworks, showing that while most of the time it seems it's just components and business logic, the reality is that under water there are many other things frameworks are handling for you, like authentication, rendering, routing, state management, i18n, styling, and more.

Steps

  1. Create a Spotify App;
  2. Authentication and authorization;
  3. Using Spotify API
  4. Creating an API Route

Create a Spotify App

First, we need to create a Spotify application to give us credentials to authenticate with the API:

  • Go to your Spotify Developer Dashboard and log in.
  • Click Create an App.
  • Fill out the name and description and click create.
  • Click Show Client Secret.
  • Save your Client ID and Secret. You'll need these soon.
  • Click Edit Settings.
  • Add http://localhost:3000 as a redirect URI.

And that is it! Uhul! 🤘

Authentication with refresh_token

There are many types of authentication, but we are going with refresh_token because we only want to authenticate once.

An iceberg of react frameworks, showing that while most of the time it seems it's just components and business logic, the reality is that under water there are many other things frameworks are handling for you, like authentication, rendering, routing, state management, i18n, styling, and more.

Authorization Code

As you can see in the above image we have everything except scopes. Scopes defines what we need fetch from the Spotify API. We'll have our application request authorization by logging in with whatever scopes we need. Here's an example of what the URL might look like. Don't forget to swap out the client_id and scopes for your own:

https://accounts.spotify.com/authorize?client_id=8e94bde7ddb84a1f7a0e51bf3bc95be8&response_type=code&redirect_uri=http%3A%2F%2Flocalhost:3000&scope=user-read-currently-playing%20user-top-read

After that, if everything is okay, 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

Now, we'll need to retrieve the refresh token. In this case, you'll need to generate a Base 64 encoded string containing the client ID and secret from earlier. You can use any tool to encode it online. The format should be client_id:client_secret. Some suggestions:

Then,

curl -H "Authorization: Basic <base64 encoded client_id:client_secret>"
-d grant_type=authorization_code -d code=<code> -d redirect_uri=http%3A
%2F%2Flocalhost:3000 https://accounts.spotify.com/api/token

This will return a JSON response containing a refresh_token.

⚠️

Important: This token is valid indefinitely unless you revoke access, so we'll want to save this in an environment variable.

Using Spotify API

To make our life easier and make the code cleaner, create environment variables for: SPOTIFY_CLIENT_ID, SPOTIFY_CLIENT_SECRET and SPOTIFY_REFRESH_TOKEN. If you've never worked with NextJs environment variables before, read this.

We can now request an access token using our client ID, client secret, and refresh_token.

// lib/spotify.js

import querystring from 'querystring'

const client_id = process.env.SPOTIFY_CLIENT_ID
const client_secret = process.env.SPOTIFY_CLIENT_SECRET
const refresh_token = process.env.SPOTIFY_REFRESH_TOKEN

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: querystring.stringify({
      grant_type: 'refresh_token',
      refresh_token
    })
  })

  return response.json()
}

Use this access_token to securely request your top tracks. This assumes you added the user-top-read scope at the beginning.

// lib/spotify.js

import querystring from 'querystring'

const client_id = process.env.SPOTIFY_CLIENT_ID
const client_secret = process.env.SPOTIFY_CLIENT_SECRET
const refresh_token = process.env.SPOTIFY_REFRESH_TOKEN

const basic = Buffer.from(`${client_id}:${client_secret}`).toString('base64')
const NOW_PLAYING_ENDPOINT =
  'https://api.spotify.com/v1/me/player/currently-playing'
const TOP_TRACKS_ENDPOINT = 'https://api.spotify.com/v1/me/top/tracks'
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: querystring.stringify({
      grant_type: 'refresh_token',
      refresh_token
    })
  })

  return response.json()
}

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

  return fetch(NOW_PLAYING_ENDPOINT, {
    headers: {
      Authorization: `Bearer ${access_token}`
    }
  })
}

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

  return fetch(TOP_TRACKS_ENDPOINT, {
    headers: {
      Authorization: `Bearer ${access_token}`
    }
  })
}

Creating an API Route

Let's test our communication with Spotify. First, create a new file at pages/api/now-playing.ts. Then, import the new function getNowPLaying.

// pages/api/now-playing.ts

import type { NextApiRequest, NextApiResponse } from 'next'
import { getNowPlaying } from '../../lib/spotify'

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

  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 isPlaying = song.is_playing
  const title = song.item.name
  const artist = song.item.artists.map(_artist => _artist.name).join(', ')
  const album = song.item.album.name
  const albumImageUrl = song.item.album.images[0].url
  const songUrl = song.item.external_urls.spotify

  res.setHeader(
    'Cache-Control',
    'public, s-maxage=60, stale-while-revalidate=30'
  )

  return res.status(200).json({
    album,
    albumImageUrl,
    artist,
    isPlaying,
    songUrl,
    title
  })
}

This returns all the song details if the user is playing the song. Otherwise, isPlaying will return false. You can handle both the conditions now. If the user is playing the song, then simply show the song otherwise display “Not Playing”.

{
  "album": "Once Upon a Time in the West",
  "albumImageUrl": "https://i.scdn.co/image/ab67616d0000b2731cb8ebda56d3dc6f926b1783",
  "artist": "The White Buffalo",
  "isPlaying": true,
  "songUrl": "https://open.spotify.com/track/6p2yQve5trvfSlpIsMLYiS",
  "title": "Stunt Driver"
}

Conclusion

Done! Now just pass this information to the interested components and be happy huhu. Any questions or suggestions, please let me know.