Maneezer is the name of a case-study currently hosted at https://maneezer.netlify.com. It’s a music streaming application integrated with Deezer that allows users to list top musics of the day, listen to their preview and manage their favorites.
If you only want to run Maneezer locally you can use any recent Node version
(14.x+). On the other hand, if you want to develop you should use the same Node
and NPM versions used by Netlify, which as of 2021-08-23 is Node v16.7.0
and
NPM v7.20.3
.
Clone the repository and run the following commands in the repository directory.
npm install && npm start
The set of tools are (in no order of importance):
I have learned tons of things in this project because I wasn’t familiar with Redux and Styled Components. On top of that, integrating with Deezer had many challenges due to its API inconsistencies and incomplete documentation.
Netlify was my service of choice to host Maneezer as a SPA. I’ve used it before, and I must say it’s definitely a tool to be loved. The netlify.toml file sets up various configuration options and in the Netlify webapp, all I did was push my commits and the deployment was done.
The Netlify project is currently configured to automatically build and deploy
when commits are integrated into the main
branch.
Users not logged in can:
- List top tracks of the day.
- Listen to track previews (limited to 30s because of the Deezer terms of use).
- Search tracks using the Deezer API.
- Display a link to the original Deezer track.
- Login using any Deezer account (notice the user will need to accept certain permissions to manage their favorites).
Users logged in can:
- List their favorite tracks.
- Add tracks to their favorites.
- Remove tracks from their favorites.
- Log out.
Other notable improvements/features:
- Responsive application.
- Display a pop-up when asynchronous operations fail, e.g. when the user tries to add to favorites a track that’s been already favorited.
- Infinite scroll.
- Client router with 404 (Not Found) page.
I wanted/had to use Axios to make HTTP requests, but it doesn’t support JSONP[1][2], which is the only way to bypass CORS when calling Deezer endpoints. The only alternative would be setting up a reverse proxy server so my server would allow requests from the client/browser to Deezer. Instead, I opted to use the Deezer Javascript SDK[3] and wrap function calls in custom promises.
[1] https://stackoverflow.com/questions/43471288/how-to-use-jsonp-on-fetch-axios-cross-site-requests
[2] https://github.com/axios/axios/blob/master/cookbook.md#jsonp
[3] https://developers.Deezer.com/sdk/javascript
I could not find in the Deezer documentation[1][2] any response field that would tell me if any given track is in the user’s favorite list. Therefore, the Top Tracks and Search pages cannot display if a track has been favorited. This in turn allows the user to attempt to favorite a track multiple times. Hopefully, the Deezer API returns an error when such attempts are made, so I decided to at least display a pop-up when this happens.
[1] https://developers.Deezer.com/api/chart [2] https://developers.Deezer.com/api/search
The Deezer SDK DZ.login
function has a bug where if the user logs in, logs out
and tries to login again (without refreshing the page), then the function does
not do anything and doesn’t call the callback. Hence there’s not much I can do
and it’s a known bug in this application.
The Deezer SDK DZ.logout
function doesn’t work as documented. It doesn’t accept a
callback function, which means the application can’t tell if the logout
operation succeeded in the server. So the best I could do was to:
- Assume the server succeeds.
- Clean the cookie because
DZ.logout
does not clear the session. - Call the undocumented function
DZ.clearDeezer
to tell Deezer to not send an invalid auth token.
The Top Tracks and Favorites pages could have their tracks’ data cached locally using the HTML Web Storage API.
There are no tests in the application, of any kind. I wanted to apply unit tests (which are the ones I have already learned) but I didn’t have enough time to finish the case study with all the features.
All HTTP requests assume they will receive a response in a reasonable amount of time. Because of that there’s no timeouts handling. Additionally, there are no retries.
As much as I wanted to enable the noImplicitAny
configuration, I had a hard
time typing some Redux Toolkit functions, like the ones created by
createAsyncThunk
and writing types for the Deezer SDK.