Spotify API Integration with NextJS: Build Your Own Now Playing
When it was time to give my portfolio a facelift (because, you know, dev life), I figured why not go big and try Next.js v14 along with the app folder structure. Rolls up sleeves, cracks knuckles—time to code! Now, let me save you some trial and error—here’s how you can do it too.
Why are we using nextJS’s API route to fetch our data?
- Server-Side Security: Sensitive credentials like
client_id
,client_secret
, andrefresh_token
should not be exposed to the client (browser). By using Next.js API routes, we can securely store and handle these values server-side, preventing them from being exposed to the public. - Token Management: By using a server-side API route, we can handle token refreshing logic securely and transparently without involving the client.
- Caching and Optimizations: With server-side requests, you can implement caching, rate limiting, and other performance optimizations like
s-maxage
andstale-while-revalidate
, which can improve performance. - Centralized Data Fetching: Fetching data from Spotify on the server ensures that all client requests go through a central point, allowing for better control over logging.
Service Architecture
Step One: Sign up as a Spotify Developer
To access your Spotify data and communicate with Spotify’s servers, you’ll need some tokens. Here’s how you can generate them:
-
Head over to Spotify for Developers and sign in with your Spotify account to register as a developer.
-
Click the Create App button and fill in the required details—here’s an example to guide you.
-
Now, go to the project you just created and make sure to note down the Client ID and Client Secret. You'll need these for authentication.
-
You should now have the following::
SPOTIFY_CLIENT_ID=<Client ID>
SPOTIFY_CLIENT_SECRET=<Client secret>
StepTwo: Generating a Refresh Token for the Spotify API
We'll need to generate a refresh token for our API so that each time a user visits the site, a new access token is created to access our Spotify data. Spotify uses the OAuth 2.0 authorization framework for this process. You can dive deeper into the details here: OAuth 2.0 Documentation.
-
To generate the authorization code with permissions to access your now-playing and top-songs data, open the following URL in your browser after replacing
<SPOTIFY_CLIENT_ID>
with your actual client ID:https://accounts.spotify.com/authorize?client_id=<SPOTIFY_CLIENT_ID>&response_type=code&redirect_uri=http%3A%2F%2Flocalhost:3000&scope=user-read-currently-playing%20user-top-read Note: Keep the same redirect_uri as the one in your spotify dashboard.
-
You’ll be redirected to the
redirect_uri
you configured, and the query parametercode
will be attached to the URL. Thiscode
is what we’ll need to generate yourSPOTIFY_REFRESH_TOKEN
.Example: http://localhost:3000/?code=AQCU5snB4rmSCKyvhWOQOEcvqsYFy17n_PcDOdoMwDVPJs
-
Head over to Base64 Encode to generate your encoded base64 string. You'll need to encode your
<SPOTIFY_CLIENT_ID>:<SPOTIFY_CLIENT_SECRET>
in this format.Encode your string in the format: SPOTIFY_CLIENT_ID:SPOTIFY_CLIENT_SECRET Note: Don't forget the ":" in between the values!
-
Final Step: Open Postman or Insomnia and paste the following cURL command. Make sure that the
redirect_uri
andcode
match the ones from the previous stepscurl --request POST \ --url https://accounts.spotify.com/api/token \ --header 'Authorization: Basic <YOUR_BASE64_STRING>' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data redirect_uri=http%3A%2F%2Flocalhost:3000 \ --data grant_type=authorization_code \ --data code=<YOUR_AUTHORIZATION_CODE>
-
And, now you’ll have the
SPOTIFY_REFRESH_TOKEN
with the response JSON:{ "access_token": "BQCE8nQzic.....rHNr", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "AQC....c5UgIxSpl8EA", "scope": "user-read-currently-playing user-top-read" }
StepThree: Writing our fetcher functions
Now let's write our fetcher functions that will handle the Spotify API requests, using the SPOTIFY_REFRESH_TOKEN
to generate an access token and fetch the necessary data.
-
Store the Spotify Tokens in the
.env
file of your nextJS project:SPOTIFY_CLIENT_ID=<Client ID> SPOTIFY_CLIENT_SECRET=<Client secret> SPOTIFY_REFRESH_TOKEN=<refresh_token>
-
Create a
spotify.ts
file inside of your nextJS project, I prefer the path to be/src/lib
and we’ll define our Spotify data-fetching functions here:getAccessToken
⇒ Retrieves a new access token using the refresh token.fetchFromSpotify
⇒ Utilizes the access token to fetch data from Spotify.
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 ENDPOINTS = {
nowPlaying: 'https://api.spotify.com/v1/me/player/currently-playing',
topTracks: 'https://api.spotify.com/v1/me/top/tracks',
topArtists: 'https://api.spotify.com/v1/me/top/artists',
}
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,
}),
})
const data = await response.json()
return data.access_token
}
const fetchFromSpotify = async (endpoint: string) => {
const access_token = await getAccessToken()
return fetch(endpoint, {
headers: {
Authorization: `Bearer ${access_token}`,
'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=30',
},
})
}
export const getNowPlaying = async () => fetchFromSpotify(ENDPOINTS.nowPlaying)
export const getTopTracks = async () => fetchFromSpotify(ENDPOINTS.topTracks)
export const getTopArtists = async () => fetchFromSpotify(ENDPOINTS.topArtists)
StepFour: Serving the Data Using Next.js API Routes
Now we’ll use Next.js API routes to serve the Spotify data. This allows you to securely fetch the data on the server-side and return it to your frontend.
Now Playing - /api/playing
Create a file at /src/app/api/playing/route.ts
, which will generate an endpoint at /api/playing
. We'll set the revalidation interval to 120 seconds, which is more than enough to keep the currently playing song updated.
import { getNowPlaying } from '@/lib/spotify'
type Artist = { name: string }
export const revalidate = 120
export async function GET() {
const response = await getNowPlaying()
if (!response.ok || response.status === 204) {
return Response.json({ isPlaying: false })
}
const song = await response.json()
const item = song.item
if (!item) {
return Response.json({ isPlaying: false })
}
const { is_playing: isPlaying } = song
const { name: title, album, artists, external_urls } = item
const artist = artists.map((artist: Artist) => artist.name).join(', ')
const albumImageUrl = album.images[0]?.url
const songUrl = external_urls.spotify
return Response.json({
album: album.name,
albumImageUrl,
artist,
isPlaying,
songUrl,
title,
})
}
Spotify Documentation for the API: https://developer.spotify.com/documentation/web-api/reference/get-the-users-currently-playing-track
Top Tracks - /api/tracks
Create a file at /src/app/api/tracks/route.ts
, which will provide an endpoint at /api/tracks
. We'll configure the revalidation interval to 86,400 seconds (1 day), since this data doesn't need frequent updates.
import { getTopTracks } from '@/lib/spotify'
type Artist = { name: string }
type Track = {
artists: Artist[]
external_urls: { spotify: string }
name: string
}
export const revalidate = 86400
export async function GET() {
const response = await getTopTracks()
const { items } = await response.json()
const tracks = items
.slice(0, 10)
.map(({ artists, external_urls, name }: Track) => ({
artist: artists.map((artist) => artist.name).join(', '),
songUrl: external_urls.spotify,
title: name,
}))
return Response.json({ tracks })
}
Spotify Documentation for the API: https://developer.spotify.com/documentation/web-api/reference/get-users-top-artists-and-tracks
Bonus: Top Artists
You can create a similar API to fetch the top artists by using Spotify's "Get User's Top Artists" endpoint. Here’s how you can set it up:
- Create a file at
/src/app/api/artists/route.ts
, which will give you an endpoint at/api/artists
. - Set the revalidation config to
86400
seconds (1 day), as the top artist data doesn’t update frequently.
StepFive: Consuming the data on the Frontend
On any page where you want to use this API, you can call the endpoint as shown in the following example:
import { SimpleLayout } from '@/components/SimpleLayout'
export const revalidate = 60 * 60 * 24
export default async function Stats() {
const response = await fetch(`${process.env.NEXT_PUBLIC_SITE_URL}/api/tracks`)
const data: TopTracks = await response.json()
return (
<SimpleLayout
title="Music"
intro="My current jam? Got it listed right here!"
>
// Your components here
</SimpleLayout>
)
}
Also, don’t miss the footer for a live update of what I’m currently listening to!