In a previous post, we discussed how to generate refresh tokens for Spotify API calls. Now, let’s dive into the exciting part: using Cloudflare Workers to interact with the Spotify API.
A Worker API to handle Spotify APIs call #
Before we begin, make sure you have a basic understanding of Cloudflare Workers and have set up a blank Worker with a configured route. If you need guidance on this, refer to our post on custom domains and routes in Cloudflare Workers.
In your wrangler.toml
file, define non-sensitive variables you’ll use in your Worker. For example, let’s specify a list of allowed origins:
[vars]
ALLOW_ORIGINS = "'https://abc.dev','http://localhost:3333'"
However, for sensitive information like your Spotify Client ID and tokens, follow these steps:
Managing Secrets #
Development Secrets #
During local development, create a .dev.vars
file at the root of your project to store secrets that will be available when running wrangler dev
. This file should follow a dotenv-like format:
SPOTIFY_CLIENT_ID=your_client_id
SPOTIFY_CLIENT_SECRET=your_client_secret
SPOTIFY_REFRESH_TOKEN=your_refresh_token
Deployed Worker Secrets #
To add secrets to the deployed Worker, run the following command in your terminal, replacing KEY
with the name of your secret:
wrangler secret put `KEY`
Calling the APIs #
Depending on your preference, you can structure your source code. For instance, I prefer splitting it into two files: index.ts
and spotify.ts
.
In spotify.ts
, you can encapsulate the logic related to Spotify API interactions. In index.ts
, you can handle the routing and overall structure of your Cloudflare Worker.
Vite/Vue Spotify components #
I want to archive the same style as my Spotify widget on my old site.
It contains a MusicEqualizer.vue
component to show an icon with a small animation to indicate a song is playing.
<script setup lang="ts">
defineProps<{
wrapperClass?: string
}>()
</script>
<template>
<div class="flex h-3 w-4 items-center gap-0.75" :class="[wrapperClass]">
<span class="h-3 w-0.75 animate-shrink rounded-sm bg-spotify-green" />
<span class="h-1.5 w-0.75 animate-expand rounded-sm bg-spotify-green" />
<span class="h-3 w-0.75 animate-shrink rounded-sm bg-spotify-green" />
</div>
</template>
Aside from custom CSS classes for animation, which are shown below, I also made use of UnoCSS/Tailwind CSS.
@keyframes shrink {
0%,
100% {
height: 0.75rem;
}
50% {
height: 0.375rem;
}
}
@keyframes expand {
0%,
100% {
height: 0.375rem;
}
50% {
height: 0.75rem;
}
}
.animate-shrink {
animation: shrink ease-in-out 1.5s infinite;
}
.animate-expand {
animation: expand ease-in-out 1.5s infinite;
}
Now, for the main component,NowPlaying.vue
:
<script setup lang="ts">
import useSWRV from 'swrv'
async function fetcher(url: string) {
const res = await fetch(url)
return res.json()
}
const { data } = useSWRV('<your-worker-route>', fetcher)
</script>
<template>
<a
class="group flex items-center font-medium !border-none"
target="_blank"
rel="noopener"
:href="data?.isPlaying && data?.songUrl ? data.songUrl : '<your-spotify-profile-url>'"
>
<svg
class="h-6 w-6 flex-none fill-zinc-500 transition group-hover:fill-spotify-logo-green"
viewBox="0 0 168 168"
>
<path d="M83.996.277C37.747.277.253 37.77.253 84.019c0 46.251 37.494 83.741 83.743 83.741 46.254 0 83.744-37.49 83.744-83.741 0-46.246-37.49-83.738-83.745-83.738l.001-.004zm38.404 120.78a5.217 5.217 0 01-7.18 1.73c-19.662-12.01-44.414-14.73-73.564-8.07a5.222 5.222 0 01-6.249-3.93 5.213 5.213 0 013.926-6.25c31.9-7.291 59.263-4.15 81.337 9.34 2.46 1.51 3.24 4.72 1.73 7.18zm10.25-22.805c-1.89 3.075-5.91 4.045-8.98 2.155-22.51-13.839-56.823-17.846-83.448-9.764-3.453 1.043-7.1-.903-8.148-4.35a6.538 6.538 0 014.354-8.143c30.413-9.228 68.222-4.758 94.072 11.127 3.07 1.89 4.04 5.91 2.15 8.976v-.001zm.88-23.744c-26.99-16.031-71.52-17.505-97.289-9.684-4.138 1.255-8.514-1.081-9.768-5.219a7.835 7.835 0 015.221-9.771c29.581-8.98 78.756-7.245 109.83 11.202a7.823 7.823 0 012.74 10.733c-2.2 3.722-7.02 4.949-10.73 2.739z" />
</svg>
<div class="ml-4 flex max-w-full items-center truncate">
<MusicEqualizer v-if="data?.isPlaying && data?.songUrl" wrapper-class="mr-2" />
<p
v-if="data?.songUrl"
class="max-w-max truncate text-sm text-zinc-800 group-hover:text-spotify-green dark:text-zinc-200 dark:group-hover:text-spotify-green"
>
{{ data.title }}
</p>
<p
v-else
class="text-sm text-zinc-700 group-hover:text-spotify-green dark:text-zinc-300 dark:group-hover:text-spotify-green"
>
Not playing
</p>
<span class="mx-2 text-sm text-zinc-400 dark:text-zinc-500">
–
</span>
<p class="max-w-max truncate text-sm text-zinc-400 dark:text-zinc-500">
{{ data?.artist ?? 'Spotify' }}
</p>
</div>
</a>
</template>
Conclusion #
I hope you have fun playing around and exploring Cloudflare Worker and Vite/Vue. You should always know there is a limit for the number of requests for a Worker in Cloudflare free account and you should take that into account when implementing it on your site.