Version v1.1.0 Released! 🎉 — Checkout the new features at GitHub

Cómo utilizar Spotify API

itscarloescp

Carlos Castillo / March 11, 2021

12 min read

How to be more productive as a programmer

Spotify API es una serie de servicios que nos proporciona esta popular aplicación de streaming de música. Nos permite acceder a información de la actividad en la plataforma, ya sean listados de las canciones más populares del momento, información sobre álbumes o artistas específicos, datos de un usuario o incluso interactuar de manera directa con la sesión del mismo, pudiendo pausar o reanudar canciones o agregar y eliminar pistas de alguna playlist, entre otras acciones.

En este post explicaré cómo consumir algunos de los endpoints que nos provee este servicio, utilizando NextJS para construir la interfaz.

Podés encontrar el código finalizado de este ejemplo en mi repositorio Tutorials.

Aplicación de NextJS

NextJS es un framework basado en React, que nos facilita la creación de "single page applications". Está pensado para mejorar el rendimiento, la seguridad y SEO de nuestras aplicaciones, así como también la experiencia de desarrollo. Otra de sus ventajas es acercar la integración de frontend y backend, ya que NextJS nos permite construir serverless functions que funcionan a manera de backend, directamente desde nuestra aplicación.

Para iniciar una aplicación básica de NextJS, corremos el siguiente comando:

npx create-next-app spotify-api

Esto crea nuestra aplicación dentro del folder llamado spotify-api en la carpeta en la que hayamos corrido el comando. Finalmente abrimos esta nueva carpeta dentro de nuestro editor de código preferido.

Antes de iniciar a correr la aplicación, debemos borrar algunos archivos que se nos generaron al momento de crearla. Primero borramos todo lo que esté dentro de la carpeta styles a excepción del archivo globals.css. En este archivo agregamos lo siguiente:

/styles/globals.css
.container {
  max-width: 720px;
  margin: auto;
  padding: 32px;

  display: grid;
  gap: 32px;
  grid-template-columns: repeat(2, 1fr);
}

ul {
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;

  list-style: none;
}

.track-item {
  cursor: pointer;
  border-radius: 8px;
  transition: background-color 150ms ease;
}

.track-item > a {
  padding: 8px 12px;
  display: flex;
  flex-direction: column;
}

.track-item:hover {
  background-color: rgb(240, 240, 240);
}

Finalmente, borramos toda la carpeta /pages/api y también podemos reemplazar todo lo que está en el archivo /pages/index.js por lo siguiente:

const index = () => <div>Tutorial Spotify - NextJS</div>

export default index

Configuración de Spotify

Para asignarle permisos que nuestra aplicación necesita para acceder a los servicios, primero debemos registrarla desde nuestra consola de desarrolladores en la pagina de Spotify Developers. Si todavía no tenemos cuenta de Spotify for Developers, debemos solicitar los permisos de desarrollador con nuestra cuenta de Spotify a través de la página para desarrolladores. Una vez nos ubicamos en el dashboard, seleccionamos "Create an app".

Una vez en el panel de nuestra aplicación, accedemos a "Edit settings" le asignamos este valor al siguiente campo:

  • Redirect URIs: http://localhost:3000

Debemos asegurarnos de seleccionar "Add" al momento de agregar el "Redirect URI", de lo contrario no se guardará.

Esto le indicará a Spotify hacia qué dirección nos redirigirá luego de hacer la autorización inicial. Finalmente, debemos configurar las credenciales con las que identificaremos a nuestra aplicación para poder consumir los servicios de Spotify: estas son las credenciales "Client ID" y "Client Secret" que podemos ver en la página de nuestra aplicación en Spotify.

Para guardar estas llaves, debemos crear un archivo llamado .env.local en la raíz de nuestra aplicación web. Dicho archivo almacenará las variables de entorno en nuestra aplicación, que son datos que solo pueden ser leídos por el servidor, de tal manera que escondemos sus valores al público. Creamos el archivo y colocamos las siguientes llaves:

/.env.local
SPOTIFY_CLIENT_ID=...
SPOTIFY_CLIENT_SECRET=...

Una vez agregamos los valores correspondientes a nuestra aplicación, podemos iniciar el servidor con el comando npm run dev, y visualizarla desde el navegador a través del URL localhost:3000.

Workflow de autorización

Para comenzar a utilizar la gran mayoría de APIs que nos proporciona Spotify (listado completo) nuestra aplicación debe contar con los permisos necesarios. La autorización en Spotify es un proceso compuesto por muchos pasos, pero en la misma guía oficial de autorización, se establecen diferentes patrones para implementar esta funcionalidad. El que nos interesa en este caso es el esquema llamado "Authorization Code Flow" que se divide en una serie de 4 pasos:

  1. Solicitar la autorización y acceder con un usuario existente que confirme esa solicitud. Este es el paso principal, y consiste en establecer la conexión inicial de nuestra aplicación con la API de Spotify.

  2. Retener el token de acceso que Spotify nos devuelve al momento de solicitar la autorización inicial. Esta es una forma para guardar registro de que nuestra aplicación ya cuenta con los permisos necesarios.

  3. Utilizar nuestro token para acceder a las rutas de Spotify Web API, y solicitar la información deseada. El token retenido sirve para confirmar los permisos que posee nuestra aplicación cada vez queremos consumir algún servicio.

  4. Refrescar el token de acceso. El token que nos fue devuelto tras la autorización inicial tiene vencimiento, así que debemos estarlo refrescando constantemente para mantener la conexión.

Solicitud de token temporal

Para hacer la primera solicitud de autorización debemos acceder a un URL específico que nos devolverá nuestro primer token temporal. Este URL está compuesto de los siguiente parámetros:

  • client_id: El valor de este parámetro debe ser el mismo valor que nos fue proporcionado anteriormente.

  • response_type: Este valor corresponde al tipo de respuesta que queremos que la autenticación inicial nos devuelva. En este caso de este parámetro va a tener el valor code.

  • redirect_uri: Corresponde al mismo URI que configuramos en el dashboard de nuestra aplicación. Es el URL al que se nos redirigirá una vez concluyamos el proceso de conexión inicial.

  • scope: Es cada uno de los accesos que queremos que se puedan acceder utilizando el token que se nos va a devolver. Una vez se nos devuelve el primer token, no podremos cambiar el scope, tendríamos que realizar el proceso de autorización inicial de nuevo para conseguir un nuevo token que tenga diferente scope. En el caso de este ejemplo solo utilizaremos un scope: "user-top-read" y "user-read-recently-played", pero existen muchos más, que podemos encontrar en el listado de scopes.

Nuestro URL final debería ser el siguiente:

https://accounts.spotify.com/authorize?client_id=<CLIENT_ID>&response_type=code&redirect_uri=http%3A%2F%2Flocalhost:3000&scope=user-read-recently-played%20user-top-read

Nos aseguramos de cambiar el campo <CLIENT_ID> por el valor asignado a nuestra propia aplicación. Luego navegamos a esa dirección desde nuestro navegador.

Si nos llegara a dar un error que nos indica que nuestro "Redirect URI" es inválido, debemos volver a la configuración de nuestra aplicación y asegurarnos que el URI que agregamos antes se haya guardado correctamente. Si generamos el URL de manera correcta se nos debería presentar una pantalla similar a la siguiente:

Página de autorización inicial

Simplemente la damos "Agree" y luego esta página nos redireccionará a nuestra aplicación, que coincide con nuestro "Redirect URI". En este URL podemos ubicar un parámetro llamado ,code", cuyo valor es nuestro token temporal inicial. Debería encontrarse de la siguiente manera:

http://localhost:3000/?code=AQCSX66S1rM3VQsfdT7bNz5bNLnzwcYDFjUr-d...

Este token es solamente temporal (lo identificaremos como TOKEN_TEMPORAL), pero lo utilizaremos para generar un token permanente.

Solicitud de token permanente

Para esto, primero tenemos que encriptar nuestras llaves de acceso ("Client Id" y "Client Secret"), utilizando el codec base64. Podemos utilizar base64encode online o alguna otra herramienta. Debemos separar los valores de nuestros nuestras llaves con dos puntos de por medio, de la siguiente forma:

<CLIENT_ID>:<CLIENT_SECRET>

Si utilizamos base64encode, debemos asegurnos de seleccionar "encoding" en la parte superior.

Ahora debemos hacer la petición para obtener el token permanente. Para esto podemos utilizar alguna herramienta como cURL desde nuestra consola o con un client online como reqbin, y corremos el siguiente comando:

curl -H "Authorization: Basic <TOKEN_BASE64>" -d grant_type=authorization_code -d code=<TOKEN_TEMPORAL> -d redirect_uri=http%3A%2F%2Flocalhost:3000 https://accounts.spotify.com/api/token

Reemplazamos <TOKEN_BASE64> con el valor encriptado que generamos recientemente, y <TOKEN_TEMPORAL> con el que habíamos obtenido en el paso anterior, el que se nos devolvía con el nombre "code" a través del URL. Al realizar esta petición se nos retornará un objeto JSON que contiene la llave refresh_token. Copiamos esa llave y la guardamos junto a las demás variables de entorno.

Si el llamado nos retorna una respuesta incorrecta y nos hemos asegurado de haber ingresado bien los campos, debemos intentar repetir el paso en el que conseguimos el TOKEN_TEMPORAL a través el URL, ya que puede ser que esta haya vencido. Cuando hagamos esto no nos volverá a salir la pantalla de autenticación, ya que nuestra sesión está activa.

SPOTIFY_CLIENT_ID=...
SPOTIFY_CLIENT_SECRET=...
SPOTIFY_REFRESH_TOKEN=...

Finalmente, reiniciamos otra vez el servidor de nuestra aplicación para que se cargue esta nueva variable. Una vez completamos este paso, hemos terminado el proceso inicial de autorización de nuestra aplicación.

Conexión con nuestra aplicación

Con las llaves de acceso que ya tenemos guardadas, podemos establecer conexión desde nuestra aplicación a cualquiera de los endpoints de Spotify API que abarquen los scopes que seleccionamos. Antes de implementar esta funcionalidad, debemos instalar dos paquetes que nos ayudaran a simplificar el establecimiento de esta conexión desde nuestra app. Para esto corremos el siguiente comando:

npm install isomorphic-unfetch@3.1.0 querystring@0.2.1

Crearemos las funciones que se encargarán de manejar dichas conexiones en una nueva carpeta llamada /lib, dentro de la raíz de nuestro proyecto. Dentro de esta carpeta creamos un archivo llamado spotify.js, y colocamos lo siguiente:

/lib/spotify.js
import fetch from 'isomorphic-unfetch'
import querystring from 'querystring'

export const getAccessToken = async () => {
  const parsedSecret = Buffer.from(
    `${process.env.SPOTIFY_CLIENT_ID}:${process.env.SPOTIFY_CLIENT_SECRET}`
  ).toString('base64')

  const res = await fetch('https://accounts.spotify.com/api/token', {
    method: 'POST',
    headers: {
      Authorization: `Basic ${parsedSecret}`,
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: querystring.stringify({
      grant_type: 'refresh_token',
      refresh_token: process.env.SPOTIFY_REFRESH_TOKEN,
    }),
  })

  return res.json()
}

export const getTopTracks = async () => {
  const { access_token } = await getAccessToken()

  const res = await fetch(
    'https://api.spotify.com/v1/me/top/tracks?time_range=short_term&limit=10',
    { headers: { Authorization: `Bearer ${access_token}` } }
  )

  return await res.json()
}

export const getRecentlyPlayed = async () => {
  const { access_token } = await getAccessToken()

  const res = await fetch(
    'https://api.spotify.com/v1/me/player/recently-played?limit=10',
    { headers: { Authorization: `Bearer ${access_token}` } }
  )

  return await res.json()
}

La primera función se encarga de almacenar un nuevo token de acceso que se utilizará para uso interno de la aplicación. Esta función primero almacena nuestro Client ID y Client Secret en una variable llamada parsedSecret. Esta es la misma operación que realizamos nosotros mismos anteriormente, sin embargo, la aplicación está utilizando variables de entorno para no exponer nuestro Client Secret al público. Una vez generamos este valor, realizamos la petición para obtener un access_token, y retornamos dicho token, que utilizaremos cada vez que hagamos una petición a cualquier endpoint de Spotify API.

Las otras funciones se encargan de hacer los llamados directamente a las APIs que retornan información. Por una parte getTopTracks nos devuelve un listado con las canciones que más hemos escuchado durante las últimas 4 semanas. En este caso estamos limitando esta lista a solo 10 canciones. La función getRecentlyPlayed nos retorna las últimas canciones que hemos escuchado, nuevamente limitando la lista a 10 canciones. Ambas funciones son muy similares, y funcionan de la misma manera, solo que consumiendo endpoints diferentes.

Ahora podemos consumir estas funciones desde cualquier lugar de nuestra aplicación. Para mostrar el listado de nuestras canciones crearemos un nuevo componente. Creamos una carpeta llamada components en la raíz de nuestra aplicación, y dentro de ella creamos un archivo llamado TrackItem.js, que contendrá lo siguiente:

/components/TrackItem.js
const TrackItem = ({ track, ranking }) => (
  <li className='track-item'>
    <a href={track.external_urls.spotify}>
      <b>
        {ranking + 1}. {track.name}
      </b>
      {track.artists.map((artist, index) => {
        return index === 0 ? artist.name : `, ${artist.name}`
      })}
    </a>
  </li>
)

export default TrackItem

Este componente va a tomar la información de cada una de las canciones y nos va a renderizar un elemento representando esta canción en una lista que vamos a generar a partir de la información que obtengamos de getTopTracks y getRecentlyPlayed. También vamos a crear un componente que nos generará estas listas con elementos TrackItem. Para esto creamos un nuevo archivo llamado TrackList.js en la misma carpeta que el componente anterior:

/components/TrackList.js
import TrackItem from './TrackItem'

const TrackList = ({ title, tracks }) => (
  <div>
    <h1>{title}</h1>
    <ul>
      {tracks.map((track, index) => (
        <TrackItem key={track.id} track={track} ranking={index} />
      ))}
    </ul>
  </div>
)

export default TrackList

Finalmente, para almacenar estos datos vamos a utilizar una funcionalidad de NextJS que nos permite pre-renderizar cada una de nuestras páginas antes de que el usuario las solicite, de esta forma cuando el usuario accede la página ya está "construída" y se cargará más rápido.

Para esto utilizaremos la función getStaticProps, que NextJS reconoce automáticamente para configurar el tipo de renderizado que deseamos, aunque también disponemos de otras modalidades. Dentro de nuestro componente principal /pages/index.js colocamos lo siguiente:

/pages/index.js
import { getTopTracks, getRecentlyPlayed } from '../lib/spotify'
import TrackList from '../components/TrackList'

const index = ({ topTracks, recentlyPlayed }) => (
  <div className='container'>
    <TrackList title='Top Tracks' tracks={topTracks} />
    <TrackList title='Recently Played' tracks={recentlyPlayed} />
  </div>
)

export const getStaticProps = async () => {
  const topTracks = await getTopTracks()
  const recentlyPlayed = await getRecentlyPlayed()

  return {
    props: {
      topTracks: topTracks.items,
      recentlyPlayed: recentlyPlayed.items.map(({ track }) => track),
    },
  }
}

export default index

Debemos sobreescribir lo que ya teníamos anteriormente dentro de `/pages/index.js

Desde getStaticProps obtenemos los resultados de las funciones que habíamos creado anteriormente, a manera de lista de canciones. La lista que nos retorna nuestras últimas canciones reproducidas contienen un formato ligeramente diferente al de la lista de canciones más escuchadas, por lo que debemos formatear su valor de retorno para poderla utilizar con el mismo componente <TrackList />. Cuando nuestro servidor recompila la aplicación, se encarga de renderizar esta página y pasa los resultados de esta función a index a manera de props.

Podemos utilizar el mismo workflow de autenticación para consumir los demás endpoints de Spotify API, e incluso existen algunos que ni siquiera requiren que estemos autenticados. Esta metodología la podemos aplicar en cualquier tipo de aplicación web, independientemente de la tecnología con la que la construyamos. Para mayor información sobre este tema visita la guía oficial para Spotify Web API.