Creating a full-fledged Spotify Playlist Generator in a Weekend
Tags: javascript, beginners, programming, webdev
Published: 2022-07-20 12:56:00

Recently, I've wanted to better understand how [APIs](https://aws.amazon.com/what-is/api/) work. I've used some simple ones that will [generate random colors](https://www.colr.org/api.html) for you, or give you a [random dog fact](https://kinduff.github.io/dog-api/) (did you know that perky-eared dogs hear sounds better than floppy-eared dogs?). APIs can be a wonderful tool to get data for your web app, but almost all of the more sophisticated ones require [authentication and authorization](https://auth0.com/docs/get-started/identity-fundamentals/authentication-and-authorization). 

To this end, I decided to use [Spotify's Web API](https://developer.spotify.com). It provides extensive documentation and allows you to do nearly anything a user can do on the platform; search for songs, create playlists, get recommendations, and much much more. 

The app I made is called Vibesition, and it allows a user to make a playlist that seamlessly transitions from one song's vibe to another. Its easily the my favorite web dev project that I have ever made. 

Try it out yourself at [vibesition.jordantwells.com](https://vibesition.jordantwells.com)! All of the source code for it can be found on my [GitHub](https://github.com/jordantwells42).




## Authentication using NextAuth.js
<img width="1267" alt="Searching for a song using Vibesition" src="https://user-images.githubusercontent.com/8213365/179861067-4cb0bac6-62da-4c8f-b975-e94bd1b7a401.png">
[NextAuth](https://next-auth.js.org) is a tool for Next.js that makes integrating with popular sign-in services such as GitHub, Facebook, Discord, and [many more](https://next-auth.js.org/configuration/providers/oauth) as simple as possible. I would highly recommend using [create-t3-app](https://create.t3.gg) to initialize your project, as it can setup much of the boilerplate of NextAuth for you.

The only changes you need to make is to the [...nextauth].ts
file, to use the [SpotifyProvider](https://next-auth.js.org/providers/spotify) and give your app the proper [authorization scope](https://developer.spotify.com/documentation/general/guides/authorization/scopes/). The Client ID and Client Secret both come from registering an application with the [Spotify Developer Dashboard](https://developer.spotify.com/dashboard/). While you're here, make sure to add something akin to "http://localhost:3000/api/auth/callback/spotify" to your Redirect URIs to ensure that you can sign in correctly.


```typescript
import NextAuth, { User, type NextAuthOptions } from "next-auth";
import SpotifyProvider, { SpotifyProfile } from "next-auth/providers/spotify";
import CredentialsProvider from "next-auth/providers/credentials";

export const authOptions: NextAuthOptions = {
  // Configure one or more authentication providers
  providers: [
    SpotifyProvider({
      authorization:
      'https://accounts.spotify.com/authorize?scope=user-read-email,playlist-modify-public',
      clientId: process.env.SPOTIFY_CLIENT_ID as string,
      clientSecret: process.env.SPOTIFY_CLIENT_SECRET as string,
    })
  ],
  callbacks: {
    async jwt({token, account}) {
      if (account) {
        token.accessToken = account.refresh_token;
      }
      return token;
    },
  },
  secret: process.env.NEXT_AUTH_SECRET as string,
};

export default NextAuth(authOptions);
```

In my case I chose a scope that allows me to the see the user's email address, and modify their public playlists, just as much as I would need for [Vibesition](https://vibesition.jordantwells.com). To actually consume this API, the authorization code needs to be exchanged for an access token which will be your ticket to use any endpoint within your defined scope. More details about this process can be found on [Spotify's documentation](https://developer.spotify.com/documentation/general/guides/authorization/code-flow/). Note that this refresh token comes from the token generated by NextAuth

```typescript
const client_id = process.env.SPOTIFY_CLIENT_ID
const client_secret = process.env.SPOTIFY_CLIENT_SECRET
const authorization_code = Buffer.from(`${client_id}:${client_secret}`).toString('base64')

const getAccessToken = async (refresh_token: string) => {
  const response = await fetch(`https://accounts.spotify.com/api/token`, {
    method: 'POST',
    headers: {
      Authorization: authorization_code,
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: new URLSearchParams({
      grant_type: 'refresh_token',
      refresh_token
    })
  })

  return response.json()
}

export const getUsersPlaylists = async (refresh_token: string) => {
  const { access_token } = await getAccessToken(refresh_token)
  return fetch('https://api.spotify.com/v1/me/playlists', {
    headers: {
      Authorization: `Bearer ${access_token}`
    }
  })
}
export const getSearch = async (refresh_token: string, query: string) => {
  const { access_token } = await getAccessToken(refresh_token)
  const querystring = new URLSearchParams({
    q: query,
    type: 'track',
    limit: '5',
    market: 'US'
  }).toString()
  return fetch(SEARCH_ENDPOINT + '?' + querystring, {
    headers: {
      Authorization: `Bearer ${access_token}`
    }
  })
}
```

Note that the "refreshToken" parameter comes from NextAuth, and with my setup can be gotten by doing.

```typescript
const token = await getToken({req});
const accessToken = token.accessToken as string;
```


## Building the App
<img width="1268" alt="Selecting a song with Vibesition" src="https://user-images.githubusercontent.com/8213365/179861119-de0b5cb3-6acc-4c39-9182-9869a91ea756.png">

Now that I had the power of Spotify's API behind me (within my small scope that I defined), I could finally get to work building my web app. 

I wanted users to be able to search songs to act as the starting and ending points for their playlist. To do this, I used the [search endpoint](https://developer.spotify.com/documentation/web-api/reference/#/operations/search) to fetch the top 5 responses from Spotify search. Once a user selects a song as shown above, it saves the Spotify ID in React state. This ID is used as a query for many of the other endpoints, so it's a good thing to save.

Once a user selects a song it automatically generates a color based on the ~vibe~ of the song using the [Spotify Audio Features](https://developer.spotify.com/documentation/web-api/reference/#/operations/get-audio-features) endpoint. Specifically, I use a combination of the song's danceability, energy, and valence (I don't know what it means either) to determine the RGB values.

To determine what songs will be in the playlist, I use the [Get Recommendations](https://developer.spotify.com/documentation/web-api/reference/#/operations/get-recommendations) endpoint. This allows you to find songs based off target features, such as danceability, tempo, key, duration and many more. In this case I use a [sigmoid-like interpolation](https://en.wikipedia.org/wiki/Sigmoid_function) to interpolate between the two song's values of the audio features and get 10 songs with target values on that curve.

Finally, to create the playlist, I use both the [create playlist](https://developer.spotify.com/documentation/web-api/reference/#/operations/create-playlist) and [add items to playlist](https://developer.spotify.com/documentation/web-api/reference/#/operations/add-tracks-to-playlist) end points. 


<img width="1268" alt="Generating a playlist with Vibesition" src="https://user-images.githubusercontent.com/8213365/179861274-ce8ad617-613d-49fc-8146-7807ce8a920a.png">


There was a whole bunch of other neat things added into this project as well. I used [Framer Motion](https://www.framer.com/motion/) to give the entire project some much needed life. I used [HTML audio tags](https://www.w3schools.com/html/html5_audio.asp) to allow users to play songs if they had a preview url from Spotify. And of course I used [Tailwind CSS](https://tailwindcss.com) to make making the UI the simplest part of the process.

## Conclusion 

Overall, this was an incredibly fulfilling project to learn about using high-impact APIs. I hope to use many more in the future and incorporate them into my projects to make getting interesting data easy. If you're interested in more of my projects or me, check out my personal website, [jordantwells.com](https://jordantwells.com). 

Thanks for reading!