Interested in this project?
Continue LearningSpotify Integration Project
Overview
For this project we are going to utilize the Spotify Web API to acquire the title, artist, and link to our currently playing song on Spotify through a Node.js script. We will then format and display the data obtained by our script in an html file, and push our project such that it is publicly hosted by Netlify via the use of serverless lambda functions.
Check out a demo of what you're about to create here! However, instead of displaying what I'm listening to on Spotify, your project will display what you are listening to.
Authentication with Spotify
First, we need to acquire some of the account information needed to access personal data via the Spotify API. To make a get request for your currently playing song, and for a majority of other requests to the Spotify API, you will need an access token. To generate your access token, you will first need a client id, client secret, refresh uri, and refresh token.
Navigate to Spotify's developer page. This will become your homepage for any applications you want to create using Spotify data. Log-in to an existing account or or create a new account (any account type will suffice). On your developer dashboard, click CREATE AN APP
, fill out the fields for App name
and App description
, check the verification statements, and create your app. This will bring you to a dashboard for the app where you can find your client id & client secret (click show client secret
to find it). Store your client id
& client secret
such that they are easily accessible for use later in the project. Click EDIT SETTINGS
and fill in the field for Redirect URIs
. Enter http://localhost:8888/callback
and store this address as your redirect uri for later use. Click SAVE
at the bottom of the pop-up screen.
The final piece of information you will need is a refresh token. Spotify, and many other services use a system of refresh and access tokens for user authentication. Given a refresh token, you are able to regenerate an access token (which invalidates in a set amount of time) as is needed to view your data through the Spotify API. Using a refresh token allows your program to restore access to your account's data without you having to manually authenticate every time. To obtain a refresh token, we're going to use Spotify's Quickstart Authentication Code.
- Install Node.js if you don't already have it.
- Clone this GitHub repository.
- Open up the
app.js
file from theauthorization_code
folder in your IDE of choice (I did everything in Visual Studio Code for this demo). - Add your client id, client secret, and redirect uri into the
client_id
,client_secret
, andredirect_uri
variables, respectively. - Find the
scope
variable and change it to"user-read-currently-playing user-read-recently-played"
. Since we want to display what we are currently listening to, we need to adjust our scopes to be reflective of this. Specifically, we need to adduser-read-currently-playing
anduser-read-recently-played
so that the access/refresh tokens returned by the script enable us to pull our currently or recently playing information. You can find the documentation for a currently playing track request here, and for a recently played track request here.
Note: We may need to access recently played song data so that your program does not display "undefined" information when you are not currently playing music.
- In your terminal, navigate into the
authorization_code
folder, and runnpm install
to install all necessary dependencies for the script to run. - Run the command
node app.js
to run the app.js file script. - Your terminal should output: "listening on 8888". In your browser navigate to http://localhost:8888/. This is the local server your script is now running on. You should see a screen that asks you to
Log in with Spotify
. Click that button, and log into the Spotify account of the data you want to display in your project. - After logging in, you should see a new screen containing the details of the Spotify app you created and the scopes we put in the script. Click
AGREE
at the bottom of the page. - A new page should again appear with some of your Spotify account information displayed. Obtain your
refresh token
from theoAuth info
section, and store it for later use.
Note: If you've run into problems with obtaining your refresh token, you can refer to this link which is Spotify's Web API Quickstart Guide. If you do follow their instructions, you will still need to change the
scope
variable to"user-read-currently-playing user-read-recently-played"
, otherwise your refresh token will not be valid for obtaining the data we want.
Set-up your Environment
Now that you have the account information needed to make a request to the Spotify API and acquire your Spotify data, you need to set up a project and script that will actually execute this.
- Create a new project folder.
- Open up your terminal, navigate to the project you've just created, and run the following commands:
Create a default package.json file for your project:
npm init -y
Installs the modules we're going to use in this application:
npm install express netlify-lambda serverless-http request dotenv
Add the following files to the root directory of your project:
- A boilerplate
index.html
file:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>*insert title here*</title>
<link href="style.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
</body>
</html>
- An empty
style.css
file. - A
.gitignore
file withnode_modules
andfunctions
added to it. When we push this repository to GitHub later, any file included in your.gitignore
file will not be pushed. - A
src
folder with a blankapp.js
file in it. - A
netlify.toml
file with the following code added to it:
[build]
functions = "functions"
Note: Because we will be hosting this application using Netlify's serverless functions, there are some special set-up items we'll have to include. This netlify.toml file will tell Netlify where to find your compiled serverless functions.
Now you're ready and set-up to write the script!
Write the Script
Open up the blank app.js
file in the src
. Here is where we're going to write the script which actually makes requests to the Spotify API and returns the currently playing data we want to display.
Express is a very commonly used Node framework for interacting with apis through requests and responses. We will be using it to interact with the Spotify API, so include the express module:
const express = require('express');
In using Netlify functions to run our script, we use something called serverless functions. Include this serverless-http
module to do so:
const serverless = require('serverless-http');
Create an express application:
const app = express();
Set up a router:
const router = express.Router();
router.get("/", (req, response) => {});
app.use("/.netlify/functions/app", router);
Finish set-up for serverless functions using the express app we created:
module.exports.handler = serverless(app);
So far, this is what the script should look like:
const express = require('express');
const serverless = require('serverless-http');
const app = express();
const router = express.Router();
router.get("/", (req, response) => {
// TODO
});
app.use("/.netlify/functions/app", router);
module.exports.handler = serverless(app);
Now we want to fill out the get route:
We need to include the request module to actually make a request to Spotify's API:
const request = require('request');
Now we're going to include our client id, client secret, and refresh token, as we will need this information to make the request:
const client_id = "your_client_id_here";
const client_secret = "your_client_secret_here";
const refresh_token = "your_refresh_token_here";
Using these account variables, we construct a new variable, authOptions
in the format that Spotify specifies which we can then use to acquire our access token:
var authOptions = {
url: 'https://accounts.spotify.com/api/token',
headers: { 'Authorization': 'Basic ' + (new Buffer(client_id + ':' + client_secret).toString('base64')) },
form: { grant_type: 'refresh_token', refresh_token: refresh_token }
};
request.post(authOptions, function(error, res) {
var header = { 'Authorization': 'Bearer ' + (JSON.parse(res.body)).access_token };
// TODO
});
At the end of this section, header
contains a formatted version of our access token.
TODO: We are ready to make a request for what we are currently playing on Spotify (this goes directly under the declaration of the header variable):
var song_name, artist, song_url;
request({ url: "https://api.spotify.com/v1/me/player/currently-playing?", headers: header }, function(error, res, body) {
if((res.statusCode == 204) || JSON.parse(body).currently_playing_type != "track") {
// you are not currently listening to Spotify, use recently played instead
request({ url:"https://api.spotify.com/v1/me/player/recently-played?type=track&limit=1", headers: header }, function(error, res, body) {
// TODO #1: recently played
});
} else {
// TODO #2: currently playing
}
});
Here we are attempting to make a request to what we are currently listening to on Spotify. In the case that there is nothing playing, or that what is playing is not a song, we instead make a new request to get the last song we listened to (using recently played). All the information we need is stored in the body of one of these requests. We just need to format and return the exact data we want and the script will be done!
Todo #1: recently played
var body_text = JSON.parse(body);
var track = body_text.items[0].track;
song_name = track.name;
artist = track.artists[0].name;
song_url = track.external_urls.spotify;
response.json({ "song_name": song_name, "artist": artist, "song_url": song_url });
Todo #2: currently playing
var body_text = JSON.parse(body);
song_name = body_text.item.name;
artist = (body_text.item.artists)[0].name;
song_url = body_text.item.external_urls.spotify;
response.json({ "song_name": song_name, "artist": artist, "song_url": song_url });
In both versions, we parse the json body returned by our request. We then extract the song_name
, artist
and song_url
from these parsed bodies and return the entire get with this data formatted as a json object.
Here is the complete script
const express = require('express');
const serverless = require('serverless-http');
const app = express();
const router = express.Router();
router.get("/", (req, response) => {
const request = require('request');
const client_id = "your_client_id_here";
const client_secret = "your_client_secret_here";
const refresh_token = "your_refresh_token_here";
var authOptions = {
url: 'https://accounts.spotify.com/api/token',
headers: { 'Authorization': 'Basic ' + (new Buffer(client_id + ':' + client_secret).toString('base64')) },
form: { grant_type: 'refresh_token', refresh_token: refresh_token }
};
request.post(authOptions, function(error, res) {
var header = { 'Authorization': 'Bearer ' + (JSON.parse(res.body)).access_token };
var song_name, artist, song_url;
request({ url: "https://api.spotify.com/v1/me/player/currently-playing?", headers: header }, function(error, res, body) {
if((res.statusCode == 204) || JSON.parse(body).currently_playing_type != "track") {
request({ url:"https://api.spotify.com/v1/me/player/recently-played?type=track&limit=1", headers: header }, function(error, res, body) {
var body_text = JSON.parse(body);
var track = body_text.items[0].track;
song_name = track.name;
artist = track.artists[0].name;
song_url = track.external_urls.spotify;
response.json({ "song_name": song_name, "artist": artist, "song_url": song_url });
});
} else {
var body_text = JSON.parse(body);
song_name = body_text.item.name;
artist = (body_text.item.artists)[0].name;
song_url = body_text.item.external_urls.spotify;
response.json({ "song_name": song_name, "artist": artist, "song_url": song_url });
}
});
});
});
app.use("/.netlify/functions/app", router);
module.exports.handler = serverless(app);
Now that it's written, we are going to check that our script returns a json object containing the data we expect it to. To do so, we need to modify our default package.json
file, replacing anything in the scripts
section with the following (between the curly braces)
"start": "./node_modules/.bin/netlify-lambda serve src",
"build": "./node_modules/.bin/netlify-lambda build src"
The start option will allow you to run the script locally, and the build option will be used by Netlify to set up your project.
In your terminal, navigate back to the root directory of your project, and run the command npm run start
to run the script. The terminal should output Lambda server is listening on 9000
, so navigate to http://localhost:9000/.netlify/functions/app where your project should be running locally. When you open the page, you should see something like this displayed in the upper left corner (instead filled in with the information of whatever song you are/were listening to)
{"song_name":"Best Of You (feat. Elle King)","artist":"Andy Grammer","song_url":"https://open.spotify.com/track/4bsqIuMHtvLCzpYAyV3044"}
Try going to your Spotify account and changing the song, then refreshing this page to see the script working and updating to match!
Now that we've verified that our script is returning the json data it should, we're going to modify our client_id
, client_secret
, and refresh token
variables to the following
const client_id = process.env.CLIENT_ID;
const client_secret = process.env.CLIENT_SECRET;
const refresh_token = process.env.REFRESH_TOKEN;
Since this is data we won't want to be publicly available in our GitHub repository, we're going to set these variables through Netlify as environment variables instead of directly writing them into our script. Make sure to continue storing the actual values of your client_id, client secret, and refresh token for later when we deploy our project.
Display your Data
Although our script is now written, we are not actually doing anything with the data it obtains when run. Here's how we can allow our index.html file to read in and display the data provided by the script
First, we want to add some code to our index.html
file as a sort of template to be filled in with the data we retrieve from the script. You can format this however you want, but here's how I did it
<p> listening to <u><a target="_blank" id="song_title"></a></u> by <span id="artist"></span> </p>
This may look a little confusing, so this is what is happening:
- All my data is displayed in a single line that looks like this: listening to insert song title here by insert artist name here
- You'll notice that my
a
tag andspan
tags currently have nothing in them. These blanks will soon be filled by the data we get from our script.
Now, we want to add a script tag to the end of the body of our index.html
file:
<script></script>
Inside the script tag, let's create a XMLHttpRequest object
var request = new XMLHttpRequest();
XMLHttpRequest objects allow us to make HTTP requests between our site and the server.
Now, we want to use our request variable to make a get request.
request.open("GET", "/.netlify/functions/app", true);
The first parameter indicates we are making a "GET"
request as we are attempting to retrieve data. The router in our script was set-up to run the get request we need at the route /.netlify/functions/app
, so as indicated by the second parameter, this is where the XMLHttpRequest object is redirected. Finally, the third parameter being true
indicates this to be an asynchronous request. This means we can wait for the request to be filled to continue with the script, and the browser will continue functioning as normal.
Next, we send our request
request.send();
Since our GET
request was made asynchronously, we need to set up an onload
function for when it returns with data
request.onload = function() {
//this function should be empty right now
}
Now, we want to fill out the onload
function so that it updates our HTML file with the information we want to display that we now have retrieved from the script and stored in request
document.getElementById("song_title").innerHTML = JSON.parse(request.responseText).song_name;
document.getElementById("artist").innerHTML = JSON.parse(request.responseText).artist;
document.getElementById("song_title").setAttribute("href", JSON.parse(request.responseText).song_url);
Each of these three lines accesses an element from the HTML file by it's id using document.getElementById("insert_id_here")
, then assigns a certain attribute of that element (inner HTML or href) to a part of the parsed JSON request object. To replace the text of a certain element in the html file, use the .innerHTML
property. This is what we did to fill out the song name and artist. To set the href for an element, use setAttribute(*attribute you want to set*, *parameter from json*)
.
Here is the completed index.html
file
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>*insert title here*</title>
<link href="style.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@200&display=swap" rel="stylesheet">
</head>
<body>
<p>listening to <u><a target="_blank" id="song_title"></a></u> by <span id="artist"></span></p>
<script>
var request = new XMLHttpRequest();
request.open("GET", "/.netlify/functions/app", true);
request.send();
request.onload = function() {
document.getElementById("song_title").innerHTML = JSON.parse(request.responseText).song_name;
document.getElementById("artist").innerHTML = JSON.parse(request.responseText).artist;
document.getElementById("song_title").setAttribute("href", JSON.parse(request.responseText).song_url);
}
</script>
</body>
</html>
Feel free to add whatever styling you want to your project! I just added the following code to my style.css
file
body {
text-align: center;
font-size: 40px;
padding: 35vh 10px 40vh 10px;
background-color: #cbe9ee;
}
#song_title, #artist {
text-decoration: none;
color: #569fac;
}
#song_title:hover {
opacity: 0.6;
}
I also changed the font, so I imported one from Google Fonts and added <link href="https://fonts.googleapis.com/css2?family=Nunito:wght@200&display=swap" rel="stylesheet">
to the head of my html file and font-family: 'Nunito', sans-serif;
to the body section of my css file.
Host your project publicly through Netlify
Now that our script is written, and our html file is set-up to display information retrieved by the script, we want to push our project to Netlify so we can display this information to anyone. This is how you are able to see whatever I am currently listening to in the demo for this project.
First, push your project to a new GitHub repository. Next, open Netlify and sign-up or log-in to an account. Navigate to your personal homepage and click New Site from Git
. Select GitHub
from the options for Continuous Deployment, then find and select the repository from GitHub you just pushed your project into. In the Basic build settings
section, fill out the Build command
as npm run build
.
Since we are inserting our client id, client secret, and refresh token into our script in the form of environment variables, we now need to add them to our Netlify project. Towards the bottom of the page, click Show advanced
.
- Add a
New variable
with KeyCLIENT_ID
and Value as your client id. - Add a
New variable
with KeyCLIENT_SECRET
and Value as your client secret - Add a
New variable
with KeyREFRESH_TOKEN
and Value as your refresh token.
Click Deploy site
and your Netlify site should deploy with a link within about a minute.
CONGRATS!! You can now share your Netlify link with anyone, and they'll be able to see whatever you're listening to on Spotify :)
Comments (0)