Spotify

Show what you're listening to.

Spotify

This snippet powers a "now playing" widget, the kind that shows the song you're currently listening to on a personal site. It calls the Spotify Web API's currently-playing endpoint from a Next.js API route and returns a clean JSON payload (title, artist, album, art, and link). The tricky part with Spotify is auth, you only authorize once, save a long-lived refresh token, and then mint short-lived access tokens on demand. Doing the token exchange on the server keeps your client secret and refresh token out of the browser.

import { getNowPlaying } from "../../lib/spotify";

export default async (_, res) => {
  const response = await getNowPlaying();

  if (response.status === 204 || response.status > 400) {
    return res.status(200).json({ isPlaying: false });
  }

  const song = await response.json();
  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;

  return res.status(200).json({
    album,
    albumImageUrl,
    artist,
    isPlaying,
    songUrl,
    title,
  });
};
// lib/spotify.js

import fetch from "isomorphic-unfetch";
import querystring from "querystring";

const {
  SPOTIFY_CLIENT_ID: client_id,
  SPOTIFY_CLIENT_SECRET: client_secret,
  SPOTIFY_REFRESH_TOKEN: refresh_token,
} = process.env;

const basic = Buffer.from(`${client_id}:${client_secret}`).toString("base64");
const NOW_PLAYING_ENDPOINT = `https://api.spotify.com/v1/me/player/currently-playing`;
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}`,
    },
  });
};

Usage

Create Spotify Application

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.

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

Authentication

There are a variety of ways to authenticate with the Spotify API, depending on your application. Since we only need permission granted once, we'll use the Authorization Code Flow.

First, 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. Swap out the client_id and scopes for your own.

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

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, we'll need to retrieve the refresh token. You'll need to generate a Base 64 encoded string containing the client ID and secret from earlier. You can use this tool to encode it online. The format should be 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 a refresh_token. This token is valid indefinitely unless you revoke access, so we'll want to save this in an environment variable.

Add Environment Variables

To securely access the API, we need to include the secret with each request. We also do not want to commit secrets to git. Thus, we should use an environment variable. Learn how to add environment variables in Vercel.

How it works

There are two files. lib/spotify.js handles authentication. It reads SPOTIFY_CLIENT_ID, SPOTIFY_CLIENT_SECRET, and SPOTIFY_REFRESH_TOKEN from the environment and builds a Base64-encoded client_id:client_secret string for HTTP Basic auth. getAccessToken POSTs to Spotify's token endpoint with grant_type=refresh_token and your saved refresh token, returning a fresh access token. getNowPlaying calls that, then requests the me/player/currently-playing endpoint with the access token as a Bearer token. This is the core idea, the durable refresh token is traded for a short-lived access token on every request, so you never store or expose the access token.

The API route consumes getNowPlaying. Spotify returns HTTP 204 No Content when nothing is playing, so the handler checks for status === 204 or any status > 400 and responds with { isPlaying: false }. Otherwise it parses the JSON and extracts is_playing, the track name, the artists (joined into a comma-separated string), the album name, the first album image URL, and the external Spotify link, then returns them as a tidy JSON object the frontend can render.

Notes & gotchas

  • Keep SPOTIFY_CLIENT_SECRET and SPOTIFY_REFRESH_TOKEN server-side only, in environment variables, never in client code or git. The whole point of the API route is to keep these off the browser; if the refresh token leaks, revoke app access and re-authorize.
  • The access token from Spotify is short-lived (about an hour). This code sidesteps that by fetching a new one on every call rather than caching, simple and reliable, though it does add a token request per page load. You can cache the access token until it expires if you want fewer round trips.
  • The refresh token is effectively permanent unless you revoke access or change the app's scopes, which is why you generate it once during setup.
  • Make sure the refresh token was issued with the user-read-currently-playing scope, otherwise the now-playing call will fail.
  • Be defensive when reading the response. Fields like song.item can be absent for some playback contexts (for example podcasts or local files), so guard against missing item/album/images before accessing them.
  • Spotify rate-limits its API. If you render this widget heavily, add caching so you don't hammer the endpoint on every visit.