restaudio-playerfeathersjsfeathers-service

Feathers custom service methods -- against the rules


The Feathers framework is designed to stick to the REST design, I know, but I think I have an edge case that is worthy of breaking the rules a bit. I want to make a music player API that allows programmatic access to the player controls via HTTP and Socket.io.

The player, however, isn't a simple case of get, put, patch, post, and delete. One player will be created for every user and will never be deleted, only altered. I want custom methods like play, pause, setQueue, etc. on subroutes of the method, for example PATCH /player/play and PATCH /player/pause.

This is exactly how Spotify does it in their API, and it makes much more sense than sending a PATCH request to the same endpoint each time and updating the player data manually, ie. PATCH /player {nowPlaying: {index: newIndex}} to skip to the next track, where the user of the API has to know the schema of the player data and how it operates. Instead, something like PATCH /player/next makes much more sense and is easier to use.

How do I implement a service like this using Feathers' API without doing it all manually in express? If it's not possible, what's the most friendly way to integrate the custom express code nicely with the rest of the app?


Solution

  • Although Spotify seems to do it that way, it is not according to the HTTP specification that states that GET is a safe method that shouldn't have any side effects other than retrieving data:

    In particular, the convention has been established that the GET and HEAD methods SHOULD NOT have the significance of taking an action other than retrieval. These methods ought to be considered "safe".

    The limitations of Feathers in that regard are very intentional to avoid common pitfalls (worst case, the Google crawler comes in and deletes a bunch of data by following the links on the page).

    You still have several options when using PATCH (or POST if you want). Create a player service based on the user id and update it state:

    PATCH /player/<userId> { "state": "playing" }
    

    Create a separate actions service that receives status changes for a player:

    POST /action { "userId": "<userId>", "state": "paused" }
    

    The advantage is that Feathers will automatically expose both those API through HTTP and websockets and you automatically get real-time updates and authentication for both, all of which you'd have to do manually in plain Express.