Luke Howsam
Software Engineer
Create an Application
Initially, we need to generate a Spotify application in order to obtain the neccesary credentials for authenticating with the API
-
Go to your Spotify Developer Dashboard & login
-
Click Create an App
-
Fill out the name and description and click create
-
Click Show Client Secret
-
Save your Client ID and secret somewhere safe, we'll need these later
-
Click Edit settings
-
Add
http://localhost:3000
as a redirect URI
You now have a correct Spotify config and the credentials to make requests
Auth
There are a few ways to authenticate with the Spotify API, depending on your app. As we require permission to be granted only once, we will use the authorization code flow: https://developer.spotify.com/documentation/web-api/concepts/authorization#authorization-code-flow
Initially, our app will gather authorization by logging in with the necesary scopes. Below is an example of what the URL will resemble. Remember to swap out the client_id
and scopes
for your own values:
https://accounts.spotify.com/authorize?client_id=1238e94b895495de7dd
b84a1f7a0e51bf3bc95be8&response_type=code&redirect_uri=http
%3A%2F%2Flocalhost:3000&scope=user-read-currently-playing%20
user-top-read
Following this, you'll be re-directed back to the redirect_uri
you specified. Within the URL, you will find a code
param. Save this value somewhere safe for later
http://localhost:3000/callback?code=mAwq3...TkDeQ
Next, we will need to obtain the refresh token. You will need to generate a Base64 encoded string that includes the client ID and secret obtained earlier. You can use a tool such as [https://www.base64encode.org/](base64 encode) to do this. The format should adhere to client_id:client_secret
.
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 refresh_token
. This token will remain valid indefinitely unless you choose to revoke access, so it's very important to keep this safe and only store it as an environment variable that is only available on the server.
Using the Spotify API
We're now able to use the Spotify API 🥳. In your Next.js app, create the following environment variables
SPOTIFY_CLIENT_ID=
SPOTIFY_CLIENT_SECRET=
SPOTIFY_REFRESH_TOKEN=
At this stage, we can now request an access token using our client ID, client secret and refresh token
const TOKEN_ENDPOINT = `https://accounts.spotify.com/api/token`;
const spotifyService = {
async getAccessToken(): Promise<{ access_token: string }> {
const response = await fetch(TOKEN_ENDPOINT, {
method: 'POST',
headers: {
Authorization: `Basic ${basic}`,
'Content-Type': 'application/x-www-form-urlencoded',
},
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
body: new URLSearchParams({
grant_type: 'refresh_token',
refresh_token,
}),
});
return response.json();
},
}
This access_token
will now securely request things such as your top tracks, now playing etc. (this assumes you added the user-top-read
scope at the beginning of the post)
Top tracks:
const NOW_PLAYING_ENDPOINT = `https://api.spotify.com/v1/me/player/currently-playing`;
async getTopTracks() {
const { access_token } = await spotifyService.getAccessToken();
return fetch(TOP_TRACKS_ENDPOINT, {
headers: {
Authorization: `Bearer ${access_token}`,
},
});
},
Creating a route handler
In order to validate that everything is set up correctly with Spotify, let's begin with creating a route handler that communicates with Spotify to get our currently playing song.
// app/api/spotify/now-playing/route.ts
import spotifyService from '@frontend/services/spotifyService';
import { SongItem } from '@frontend/types/spotify';
export const runtime = 'edge';
export const revalidate = 10;
export async function GET() {
const res = await spotifyService.getNowPlaying();
if (res.status === 204 || res.status > 400) {
return new Response(JSON.stringify({ isPlaying: false }), {
status: 200,
});
}
const song = (await res.json()) as SongItem;
if (!song.item) {
return new Response(
JSON.stringify({
isPlaying: false,
}),
{
status: 200,
},
);
}
const isPlaying = song.is_playing;
const title = song.item.name;
const artist = song.item.artists
.map((_artist: { name: string }) => _artist.name)
.join(', ');
const album = song.item.album.name;
const albumImageUrl = song.item.album.images[0].url;
const songUrl = song.item.external_urls.spotify;
const body = JSON.stringify({
album,
albumImageUrl,
artist,
isPlaying,
songUrl,
title,
});
console.log(body)
return new Response(body, {
status: 200,
});
}
This handler will return the currently playing song, formatted to satisfy eslint / remove unneccesary info. If everything is working correctly, you should see some data in the console like the following after running next dev
and hitting the endpoint when you're listening to a song on Spotify :)
{
"album": "Jongen Van De Straat",
"albumImageUrl": "https://i.scdn.co/image/ab67616d0000b2738d30694491e182ea7e38f517",
"artist": "Lil Kleine",
"isPlaying": true,
"songUrl": "https://open.spotify.com/track/3bhmMwkuZFhgUr9Y6hmjze",
"title": "Heel Mijn Leven"
}