Skip to content
Philip Bulley edited this page May 10, 2016 · 13 revisions

Socket vs RESTful HTTP

It is tempting to just abandon REST and do everything via web sockets as we're probably not so dependent on client-side caching of API responses. But at the same time it's probably counter intuitive to re-invent what we get with RESTful architecture for basic CRUD operations. So, I'm thinking we use REST for the usual CRUD operations on our models and use web sockets for relatively lightweight UI updates of data (in most cases already loaded via REST API) and everything to do with playback controls + the currently playing track.

REST Endpoint Overview

/api/playlists

GET

  • Get all playlists/rooms to display on the playlist menu page.

/api/playlists/:playlistId

GET

  • Get all playlist data including full track listing.
  • Use when entering a playlist/room.

PUT / PATCH

  • Updates fields on a playlist.
  • Supports partial objects (ie. only fields which require updating)

DELETE

  • Remove a playlist from the app.
  • Not sure where this functionality will live in the UI? Should there be any permissions restriction on deleting playlists? Only allow deletion of playlists with no current users?

/api/playlists/:playlistId/tracks

POST

  • Add a track to a playlist using the track's foreign ID (body contains provider and foreignId)

/api/track/:track_id/artwork

GET

  • Gets the cover artwork image for a track

/api/search/:terms

GET

  • Gets the results of a search based on the terms

/api/users

GET

  • Get list of users

/api/me

GET

  • Get user information for authenticated user

/auth/:provider?redirect=:url

GET

  • Authenticate user with oauth provider and redirect to url

Note: There is no logout required on the API, as API access is stateless

Web Socket Event Overview

Client -> Server

playlist.play

  • Starts playback of a playlist referenced by it's ID
  • Payload: playlistId

playlist.pause

  • Pauses playback of a playlist referenced by it's ID
  • Payload: playlistId

playlist.track.upvote

  • Upvotes a track on a playlist

playlist.track.veto

  • Vetos a track on a playlist

user.enter

  • Emitted when a user has received the socket connect event

system.volume (unimplemented)

  • Allows client to control the output volume (See #5)

Server -> All clients

playlist.play

  • When a playlist resumes playback

  • Payload:

      {
        playlistId, 
        playlistTrackId, 
        trackId
      }
    

playlist.pause

  • When the playlist is paused

  • Payload:

      {
        playlistId, 
        playlistTrackId, 
        trackId
      }
    

playlist.track.start

  • When a track begins playback from the start

  • Payload:

      {
        playlistId, 
        playlistTrackId, 
        trackId
      }
    

playlist.track.progress

  • Emitted as a track progresses

  • Payload:

      {
        playlistId, 
        playlistTrackId, 
        trackId,
        currentTime,
        duration,
        progress
      }        
    

playlist.end

  • Emitted when a playlist ends
  • Payload: playlistId

playlist.tracks.change (unimplemented)

  • When the playlist order has been altered due to an upvote, completion, veto, adding or removing a track

  • Payload:

      {
        action,           // {string}            A string describing reason for update: complete|upVote|veto|add|remove
        playlistId,       // {string}            The ID of the affected playlist
        playlistTrack,    // {PlaylistTrack[]}   The PlaylistTrack the action has been performed upon
        playlistTrackIds  // {string[]}          Array of IDs in the correctly sorted order
      }
    

    Client can use playlistTrackIds to re-order/add to/remove from existing playlist.

  • 💡 We could send out super-lightweight deltas to update client state, but currently thinking we go with an approach where playlist ordering is explicit to avoid any concurrency/race condition style issues.

user.update

  • When a user enters or exits the app.
  • Payload: Array of User objects (without any private tokens, etc). Client can diff the before/after to determine user enter/exit.

system.volume (unimplemented)

  • When the output volume of the server has been set/changed (See #5)

playlist.track.upvote (deprecated in favour of playlist.tracks.change)

  • When a track is upvoted
  • Payload: track object, user object

playlist.track.veto (deprecated in favour of playlist.tracks.change)

  • When a track is vetoed
  • Payload: track object, user object

Server -> Single client

connect

  • Emitted to user when they connect to the socket
  • Payload: TBD current state of app (playlist, track etc)

error.playlist.track.upvote

  • Emitted to user if their upvote failed due to error

error.playlist.track.veto

  • Emitted to user if their veto failed due to error