How to integrate Spotify into your web app

2024-06-26

I wanted to display what song I'm currently listening to on my site. Since I'm always listening to Spotify, this is the API I'm had to integrate. This post will be a quick rundown of the steps needed to integrate Spotify.

Here's a visual representation of the Spotify integration:

Spotify Integration Example

And here are the steps needed to integrate:

Create an Application

First, create a Spotify application for authentication credentials.

  • Go to the Spotify Developer Dashboard and log in.
  • Click Create an App, fill in the form and click create.
  • Click Show Client Secret to get your Client ID and Secret.
  • Add http://localhost:3000 as the redirect URI in the settings.

Now you have a Spotify application with the necessary credentials.

Authentication

We'll use the Authorization Code Flow for one-time permission.

  1. Request authorization with the following URL (replace client_id and scopes):
https://accounts.spotify.com/authorize?client_id=1abe4bac63b1436e8e809
8fd25088ced&response_type=code&redirect_uri=http://localhost:3000&
scope=user-read-currently-playing%20user-top-read
  1. After authorizing, you'll be redirected to your redirect_uri with a code query parameter. Save this value.

  2. Retrieve the refresh token using a Base 64 encoded string of your client ID and secret (To encode the strings, I used this tool):

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

Save the refresh_token from the JSON response in an environment variable.

Using Spotify's API

Add these values to your .env file in the app:

SPOTIFY_CLIENT_ID=
SPOTIFY_CLIENT_SECRET=
SPOTIFY_REDIRECT_URI=

Request an access code with your credentials:

 
import { URLSearchParams } from 'url';
 
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 TOKEN_ENDPOINT = 'https://accounts.spotify.com/api/token';
 
export const getSpotifyAccessToken = async (): Promise<string> => {
  const basic = Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString('base64');
 
  const params = new URLSearchParams({
    grant_type: 'refresh_token',
    refresh_token: REFRESH_TOKEN,
  });
 
  try {
    const response = await fetch(TOKEN_ENDPOINT, {
      method: 'POST',
      headers: {
        Authorization: `Basic ${basic}`,
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: params,
    });
 
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
 
    const data = await response.json();
    return data.access_token;
  } catch (error) {
    console.error('Error fetching access token:', error);
    throw error;
  }
};

Use the access_token to fetch the track you currently listen to:

export type SpotifyNowPlaying = {
	albumImageUrl: string;
	artist: string;
	isPlaying: boolean;
	songUrl: string;
	title: string;
	timePlayed: number;
	timeTotal: number;
	artistUrl: string;
};
 
export const getSpotifyNowPlaying = async (): Promise<SpotifyNowPlaying> => {
	const { access_token } = await getSpotifyAccessToken();
 
	const response = await fetch(NOW_PLAYING_ENDPOINT, {
		headers: {
			Authorization: `Bearer ${access_token}`,
		},
	});
 
	const song = await response.json();
	const albumImageUrl = song.item.album.images[0].url;
	const artist = song.item.artists.map((artist) => artist.name).join(", ");
	const isPlaying = song.is_playing;
	const songUrl = song.item.external_urls.spotify;
	const title = song.item.name;
	const timePlayed = song.progress_ms;
	const timeTotal = song.item.duration_ms;
	const artistUrl = song.item.album.artists[0].external_urls.spotify;
 
	return {
		albumImageUrl,
		artist,
		isPlaying,
		songUrl,
		title,
		timePlayed,
		timeTotal,
		artistUrl,
	};
};

That's it! You now have the data. Now to the actual component to display that song.

The component I ended up making:

export const NowPlaying = (spotifyNowPlaying: SpotifyNowPlaying) => {
	if (!spotifyNowPlaying) return null;
 
	return (
		<div className="grid text-center lg:mb-0 lg:grid-cols-2 lg:text-left ">
			{!spotifyNowPlaying.isPlaying && (
				<div className="flex items-center gap-2">
					<SpotifyIcon className="text-[#1DB954]/75" />
					<span className="text-gray-500 dark:text-gray-400">
						Currently offline
					</span>
				</div>
			)}
			{spotifyNowPlaying.isPlaying && (
				<a
					href={spotifyNowPlaying.songUrl}
					target="_blank"
					rel="noreferrer"
					className=""
				>
					<div className="flex items-center gap-2">
						<SpotifyIcon className="text-[#1DB954]/75" height={18} width={18} />
						<span className="text-gray-500 dark:text-gray-400">
							Now playing
						</span>
					</div>
 
					<div className="flex items-center gap-2 rounded-lg border border-transparent px-5 mr-2 mt-2 py-4 transition-colors border-gray-300 hover:bg-gray-100 dark:border-neutral-700 hover:dark:bg-neutral-800/30">
						<Image
							className="rounded"
							width={52}
							height={52}
							src={spotifyNowPlaying.albumImageUrl}
							alt={`${spotifyNowPlaying.title} album art`}
						/>
						<div>
							<div className="flex items-center gap-2">
								<PlayingAnimation />
								<div>{spotifyNowPlaying.title}</div>
							</div>
							<p className="text-gray-500 dark:text-gray-400">
								{spotifyNowPlaying.artist}
							</p>
						</div>
					</div>
				</a>
			)}
		</div>
	);
};

If you paid close attention there is also a moving Spotify animation next to the song. I took a lot of inspiration from this article. I copied the design and the animation, but adapted it to use tailwindcss.

That's really it! Enjoy :-)