Created
February 27, 2021 11:14
-
-
Save GregBrimble/a5cfdeb78b7c1b77e351ee32cf758d70 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
addEventListener('fetch', (event) => { | |
event.respondWith(handleErrors(handleRequest, event.request)) | |
}) | |
const handleRequest = async (request) => { | |
const url = new URL(request.url) | |
switch (url.pathname) { | |
case '/': { | |
const newReleases = await spotify('/new-releases') | |
// If we want to return just the data we get back from Spotify, | |
// we can just return the following: | |
// return new Response(JSON.stringify(newReleases, { headers: { 'Content-Type': 'application/json' }})) | |
// But instead, let's render it nicely-ish: | |
return new Response(`<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"/> | |
<title>Spotify's New Releases</title> | |
</head> | |
<body> | |
<h1>New Releases on Spotify</h1> | |
<ul> | |
${newReleases.albums.items.map((album) => `<li>${renderAlbum(album)}</li>`)} | |
</ul> | |
</body> | |
</html>`, { headers: { 'Content-Type': 'text/html' }}) | |
} | |
default: { | |
return new Response("Not Found", { status: 404 }) | |
} | |
} | |
} | |
/*** | |
* A collection of helper functions for rendering out the page. | |
*/ | |
const renderLinkable = (item) => `<a href="${item.external_urls.spotify}">${item.name}</a>` | |
const renderAlbumArtwork = (album) => `<img src="${album.images[0].url}" width="250" height="250" />` | |
const renderAlbum = (album) => `<div>${renderAlbumArtwork(album)}<p>${renderLinkable(album)} — ${album.artists.map((artist) => renderLinkable(artist)).join(', ')}</p></div>` | |
/*** | |
* Since we might want to make multiple requests to Spotify, | |
* let's abstract out something which can call the Spotify API for us. | |
*/ | |
const BASE_URL = 'https://api.spotify.com/v1/browse' | |
const spotify = async (path) => { | |
const url = `${BASE_URL}${path}` | |
const accessToken = await getAccessToken() | |
const response = await fetch(url, { | |
headers: { | |
'Authorization': `Bearer ${accessToken}` | |
} | |
}) | |
return await response.json() | |
} | |
/*** | |
* Authorization | |
* | |
* We're following the simplest 'client credentials' flow: | |
* https://developer.spotify.com/documentation/general/guides/authorization-guide/#client-credentials-flow | |
*/ | |
const AUTHORIZE_URL = 'https://accounts.spotify.com/api/token' | |
const getAccessToken = async () => { | |
const clientCredentials = btoa(`${SPOTIFY_CLIENT_ID}:${SPOTIFY_CLIENT_SECRET}`) | |
const response = await fetch(AUTHORIZE_URL, { | |
method: 'POST', | |
headers: { | |
'Authorization': `Basic ${clientCredentials}`, | |
'Content-Type': 'application/x-www-form-urlencoded' | |
}, | |
body: new URLSearchParams({ | |
grant_type: "client_credentials" | |
}) | |
}) | |
const json = await response.json() | |
return json.access_token | |
} | |
/*** | |
* Error handling | |
* | |
* In case there's a problem, let's return a readable error message. | |
*/ | |
const handleErrors = (func, request) => { | |
try { | |
return func(request) | |
} catch (error) { | |
const { message, stack } = error | |
return new Response(JSON.stringify({ message, stack }), { headers: { 'Content-Type': 'application/json' }}) | |
} | |
} | |
// Polyfill for btoa (ignore me!) | |
const btoa = (data) => { | |
const ascii = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' | |
const len = data.length - 1 | |
let i = -1 | |
let b64 = '' | |
while (i < len) { | |
const code = data.charCodeAt(++i) << 16 | data.charCodeAt(++i) << 8 | data.charCodeAt(++i); | |
b64 += ascii[(code >>> 18) & 63] + ascii[(code >>> 12) & 63] + ascii[(code >>> 6) & 63] + ascii[code & 63]; | |
} | |
const pads = data.length % 3; | |
if (pads > 0) { | |
b64 = b64.slice(0, pads - 3); | |
while (b64.length % 4 !== 0) { | |
b64 += '='; | |
} | |
} | |
return b64; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment