Apple Music JavaScript integration guide
We all know music streaming is booming right now. Because of the sheer convenience, and ease-of-use users have fallen in love with this modern way of listening to music.
But what does it mean for web developers? Well, unless you’re Spotify, Apple, Amazon, or similar, your chances of competing in “streaming wars” are relatively low. But what if your goal is different? What if, instead of a streaming platform itself, you want to make a music player, add-on service, or something like that? In this case, why not let “the big players” help you?
In today’s post, I’d like to walk you through the process of integrating Apple Music right into your web app with Apple’s MusicKit JS. Sounds interesting?
Why Apple Music?
Before we dive in, I’d like to address the possible question of:
Why did I choose Apple Music?
You might already know that Spotify is the market leader. It has by far the largest user base while also having the highest number of paid subscribers (although the latter is much smaller). Apple Music lags behind Spotify in both categories, landing in 2nd place.
So, if we were choosing based-off user-base, we should go for Spotify. However, there’s something more you have to keep in mind. That is the service’s public API.
In this aspect, Apple, with its MusicKit, is definitely on top. It allows you to integrate with Apple Music right from your iOS, Android, or web app through easy-to-use libraries.
If you’re interested, Spotify does provide similar functionality through its Web Playback SDK. Still, it’s currently in beta with limited support, works only for the paid subscribers, and requires Spotify’s written approval before being used in commercial products. That’s far from Apple’s offering, but still quite good considering most of (if not all) the other streaming services don’t provide such an option at all.
Preparations
Before getting to work with MusicKit JS, we’ll have to do some preparations. That is, get ourselves an Apple Music API Token.
Surprisingly, this can be a bit daunting of a task, especially with Apple’s pretty convoluted documentation.
First and foremost, you’ll need to be a member of the Apple Developer Program. It’ll require elaborate sign-up and a payment of 100 USD/year fee.
With that done, we’ll need to go to the Apple Developer dashboard and collect a few details:
- From the Membership tab, get your Team ID.
- Next, we’ll need a MusicKit Identifier (one per app):
- Go to the Certificates, Identifiers & Profiles tab.
- There, enter the Identifiers tab.
- Click the ”+” button next to the header (if you’re in a team, it requires Admin or Account Holder permissions to show up).
- Enter your App name and “reverse-domain-style” name for the Identifier and hit “Continue”, then “Register”.
- With proper Identifier already set up, we can now create a Private Key to access the MusicKit service:
- From Certificates, Identifiers & Profiles, go to the Keys tab (visible only for Admin or Account Holder)
- Click the ”+” button, enter required details and check the MusicKit checkbox (if you have multiple MusicKit Identifiers, you’ll need to select one of them)
- Click “Confirm” and download the .p8 key file (it’s downloadable only once, so keep it safe!)
- The file’s content is your Private Key, while the file’s name should contain the Key ID as the last ten characters in the file name (right after the underscore). If you changed the filename, Key ID should also be available in the Developer dashboard.
Quite a lot of stuff here, and yet we’re still not done.
Only now, with all the details, we can finally generate the JSON Web Token (JWT) required to access the service.
For that, we’ll use some JS. NodeJS to be specific with jsonwebtoken module, and the following code:
const fs = require("fs");
const jwt = require("jsonwebtoken");
const privateKeyPath = "./AuthKey_12345ABCDE.p8";
const privateKey = fs.readFileSync(privateKeyPath).toString();
const teamId = "ABCDE12345";
const keyId = "12345ABCDE";
const token = jwt.sign({}, privateKey, {
algorithm: "ES256",
expiresIn: "180d",
issuer: teamId,
header: {
alg: "ES256",
kid: keyId,
},
});
console.log(token);
With the above code, we generate and output the required JWT token to the console.
On a side-note, how you’ll manage your JWT tokens is up to you. In our case, we’ll need only a single token for the demonstration purposes, and that’s why it’s set to be valid for 180 days (max value).
Integration
Alright, so it was quite a lot of work to get the token, but it’ll only get easier from here. Let’s finally do some coding!
Installation
First, we have to “install” the MusicKit JS library. The only official way to do so is through a <script>
tag pointing to Apple’s CDN:
<script src="https://js-cdn.music.apple.com/musickit/v1/musickit.js"></script>
Now, to some web devs out there (including me), this might feel a bit dated. To ease-out the development process, you might look for some 3rd-party NPM module-based integration, but I preferred to leave it as is, with some TypeScript typings on top.
If you’re a TypeScript user, typings can help you navigate through any library much easier. Sometimes better than the official documentation!
As for MusicKit JS, there are some typings openly available. They aren’t perfect and a bit dated but could be helpful nonetheless.
Initialization
With the library installed, we should now initialize it. There are two ways to do it - let’s call them “the HTML” and “the JS way”.
HTML way
In the first one, you’re giving more control to the library by simply letting it know what to do through a couple of HTML tags. For the setup, just add some <meta>
tags to the <head>
:
<meta name="apple-music-developer-token" content="DEVELOPER-TOKEN">
<meta name="apple-music-app-name" content="MusicKit Web App">
<meta name="apple-music-app-build" content="1.0.0">
After that, the user will need to authorize with Apple Music, which the library can also handle for you if you simply show it which elements it should attach to through different id
s:
<button id="apple-music-authorize"></button>
<button id="apple-music-unauthorize"></button>
JS way
While the HTML setup can be convenient, I prefer a bit more verbose way, where I feel more in control of what’s happening. That’s why, from this point on, I’ll cover JS exclusively, but you can find more about HTML way in the official docs.
First, you need to listen to the musickitloaded
on the document
element, to know when the library is ready for use. Personally, I prefer to wrap it in a Promise, to limit the potential of “callback hell” and achieve cleaner code:
const setupMusicKit = new Promise((resolve) => {
document.addEventListener("musickitloaded", () => {
const musicKitInstance = window.MusicKit.configure({
developerToken: "DEVELOPER-TOKEN",
app: {
name: "MusicKit Web App",
build: "1.0.0",
},
});
delete window.MusicKit; // clear global scope
resolve(musicKitInstance);
});
});
When we know that the library has loaded, we use the configure()
method of the global MusicKit object to create and configure an actual MusicKit instance that we’ll use later on (similarly to the <meta>
tags in the HTML setup).
In the code above, it’s this instance that I resolve from the promise. I also clean the global scope from the MusicKit object, as it’ll no longer be necessary.
To authorize the user, we can use the authorize()
method, which returns a promise for when the process is done.
// ...
setupMusicKit.then(async (musicKit) => {
try {
await musicKit.authorize();
// await musicKit.unauthorize();
} catch(error) {
// Handle cases when authorization fails
}
})
Usage
At this point, the documentation and optional typings should be your guide. MusicKit JS gives you access to pretty much all the functionalities you get from the Apple Music app itself - controlling music playback, searching through Apple Music’s catalog, manipulating user’s personal library & more!
With that said, let’s take a look at a few example use-cases of MusicKit JS possibilities.
Playing one of the user’s playlists
// ...
// null indicates fetching all the playlists (within pagination limit)
const playlists = await musicKit.api.library.playlists(null);
const randomIndex = Math.floor(Math.random() * playlists.length);
// pick random playlist
const playlistId = playlists[randomIndex].id;
// set playback queue
await musicKit.setQueue({
playlist: playlistId,
});
// start playing (playing audio usually requires user interaction)
await musicKit.play();
// ...
Controlling music playback
// ...
const play = () => {
return musicKit.play(); // promise
};
const pause = () => {
appleMusic.pause();
};
const next = () => {
return appleMusic.skipToNextItem(); // promise
};
const previous = () => {
appleMusic.skipToPreviousItem(); // promise
};
// ...
It’s important to note that there seems to be an error with the MusicKit library while moving to the previous song (skipToPreviousItem()
). It results in an error being thrown, as well as an error dialog with a message like:
A play stop() method was called without a previous play() descriptor
Apart from false warnings, everything should be working fine. So, as it’s a library error, your best choice would be to hide the dialog with some CSS:
##musickit-dialog-scrim,
##musickit-dialog {
display: none;
}
Searching through Apple Music’s catalog
// ...
const { songs } = await appleMusic.api.search("Counting Stars", {
limit: 1,
types: "songs",
});
if (songs && songs.data[0]) {
await appleMusic.playNext({ song: resources.songs.data[0].id });
}
// ...
When using search()
as well as other querying methods, it’s worth knowing that they accept a parameter called queryParameters
. It’s an object of parameters that are passed to the API call underneath, in line with Apple Music API docs (which are separate from MusicKit JS docs). Again, Apple’s docs are pretty convoluted, so you’ll need to find your way around.
There’s more…
So, these are only a few common examples of what you could do with MusicKit JS. It gives you broad access to Apple Music’s potential, and we’ve only scratched the surface. Now you can go explore more and build your own Apple Music integration!
If you’ve got any questions or got stuck in the docs - feel free to leave a comment down below, and I’ll try to help.
For more web development goodness, follow me on Twitter, Facebook, or through my newsletter. Also, consider checking out my YouTube channel (it’s making a come-back!) and subscribe over there. Thanks for reading and have a good listen… sorry, coding!
If you need
Custom Web App
I can help you get your next project, from idea to reality.