So you want to use an API to get some data which you want to render as HTML, well that should be easy here in 2019. I want to try to do it in less than a minute of coding. Writing this post of cause adds some time to this equation. But lets go!
What to show?
I just had breakfast, listened to some Simon & Garfunkel, and wanted to test just how good the Apple Music text search was. It kind of failed to locate the song I was listening to (The Boxer) based on part of the lyrics, which annoy and disappointed me a bit: “What about an API to do the search?” I thought.
A simple Google search lead me to a simple query based API from Apple. That is what I now want to create a simple web app for, and also use to run you through the simplest non framework small piece of code.
The API skeleton
The Google search lead me to a page with examples of using the Apple Music (iTunes) query based API. I want to be able to do a search using a term
and here is an example:
https://itunes.apple.com/search?term=simon+garfunkel
Just entering the URL into a browser will show you the raw result. Here is part of the result, which is raw JSON:
{
"resultCount":50,
"results": [
{"wrapperType":"track", "kind":"song", "artistId":461503, "collectionId":192480256, "trackId":192480260, "artistName":"Simon & Garfunkel", "collectionName":"Sounds of Silence", "trackName":"The Sound of Silence", "collectionCensoredName":"Sounds of Silence", "trackCensoredName":"The Sound of Silence", "artistViewUrl":"https://music.apple.com/us/artist/simon-garfunkel/461503?uo=4", "collectionViewUrl":"https://music.apple.com/us/album/the-sound-of-silence/192480256?i=192480260&uo=4", "trackViewUrl":"https://music.apple.com/us/album/the-sound-of-silence/192480256?i=192480260&uo=4", "previewUrl":"https://audio-ssl.itunes.apple.com/itunes-assets/Music5/v4/92/d0/9f/92d09f55-ce0d-0cd9-fbdd-4f2b83f3908a/mzaf_9048078927834096686.plus.aac.p.m4a", "artworkUrl30":"https://is4-ssl.mzstatic.com/image/thumb/Music113/v4/fd/cd/8c/fdcd8cda-01d5-d303-b846-45fe5e4b2343/source/30x30bb.jpg", "artworkUrl60":"https://is4-ssl.mzstatic.com/image/thumb/Music113/v4/fd/cd/8c/fdcd8cda-01d5-d303-b846-45fe5e4b2343/source/60x60bb.jpg", "artworkUrl100":"https://is4-ssl.mzstatic.com/image/thumb/Music113/v4/fd/cd/8c/fdcd8cda-01d5-d303-b846-45fe5e4b2343/source/100x100bb
That was easy! Now we have the datasource, we just need to do a fetch
.
Create POC on codepen.io
Normally I use codepen.io when I do POCs (Proof Of Concept) and I spin up a vanilla POC with these lines of JavaScript:
const url = "https://itunes.apple.com/search?term=simon+garfunkel";
let html = "loading...";
const eleApp = document.getElementById("app");
eleApp.innerHTML = html;
fetch(url)
.then(response => {
html = response;
})
.catch(response => {
html = response;
})
.finally(() => {
eleApp.innerHTML = html;
});
Now we have the JSON
displayed on the page, right? Well, sorry CORS
blocks us and we for now get this response: TypeError: Failed to fetch
which, if you open the developer toolbar console has a more detailed description like this:
Access to fetch at 'https://itunes.apple.com/search?term=simon+garfunkel' from origin 'https://cdpn.io' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Getting around CORS
It is with good intention that browsers blocks javascript from doing uncontrolled Cross Origin Requests, as it would lead to uncontrolled behaviour on the web.
Option A - say please to the API provider
Your options are to make the API endpoint add some headers to their response, like this: Access-Control-Allow-Headers: *
but in this case, I chose to rule out the option to make Apple open their API for any web traffic :-)
Option B - create or use a proxy
You can also add a proxy of your own, a middle way endpoint, which is running on a server you controle. You can then do the request from a server which does not have the same CORS restrictions. The response the proxy receive could then be using CORS headers which allow your site to fetch any content. It can be done relative simple and even cost free if you use sites like heroku.com and other serverless services.
Option C - google for a website which can serve as a proxy
I have played around with online free proxies, but my experience is that they are not stable, and do I really want my content to go through some website? Not that I have secrets in POCs, but...
Thanks to Heroku, Express and Node
I decided to go for solution B: spin up a simple Express server running on Node and hosted on Heroku.com. The code is not rocket science (if you have been around for a while here in front-end/full-stack world). The Node app is using Express as a web server, and as Heroku allows you to deploy repositories automatically from a branch on Github, this solution cost nothing - except from your time :-)
I will not go into details, but instead let you get a link to the actual repository I am using to build a simple proxy: no-no-cors
The repository is hosted on Heroku on a free basis: no-no-cors.herokuapp.com
If you look in the source code you can see that I filter on the origin
of the requester, allowing me to only enable CORS for request coming from codepen or localhost (so I can test the app locally).
Codepen - the UI
The last part is building a web app, a front-end. I have played a bit around with it, which is also why I removed part of the original title (the in 3 minutes). If I did not care about how the view layer looked, and if CORS did not force me to create a backend, I guess that I could have kept that last part of the original blog post title :-)
That's it!
I guess that I now have created an okay search for music on music, using only free resources:
- Backend: Heroku.com
- Code repository: Github.com
- Frontend: Codepen.io
- API: itunes.apple.com/search
- Blogging: dev.to! :-)
- Created CSS Grid using grid.layoutit.com
Please feel free to comment if you have something to say about my litle POC! :-)
Try it on CodePen.io
Top comments (0)