Need advice on avoid thinking in "actions"


#1

Hi, currently I’m trying to design an API which allows to remote control a server. This API provides an “Update” API meaning that a client can trigger via this API the download and installation of updates on the server. But often I find myself in thinking in actions like “start download”, “start update”, “abort update”, … so I’m wondering if you can give me some advice on how I can define such kind of API with JSON API without “actions”.

In particular I’m interested in the use-case “Start/Stop download”

So far I’ve defined the following:

Collection /updates of type updates with attributes size, status and a to-one relationship to downloads. The status can be PENDING, DOWNLOADING, INSTALLING, …

Collection /downloads of type downloads with attributes progress, timeremaining, status. The status can be ACTIVE, PAUSED, COMPLETED, …

The downloads resource object for a given update should be kept until the update has been completed or a client explicitly wants to remove it beforehand (e.g. to make space for other updates…).

The solutions I came up with so far to trigger a download are:

  1. Issue a PATCH /updates/<id> and update status to DOWNLOADING. Upon this state change the server should automatically create a downloads resource object and add the corresponding relationship to the corresponding updates resource object.

  2. Create a new empty downloads resource object via POST /downloads and add this object as relationship via POST /updates/<id>/relationships/download. The server should then automatically update the status of the corresponding updates resource object to DOWNLOADING.

… and aborting an download could be realized for both solutions via a DELETE /downloads/<id>.

First solution requires only one HTTP request but it’s somehow not very intuitive as the client has to manually modify the status. Also it smells a bit like firing actions via a status attribute and the server has to evaluate if a state change PENDING -> DOWNLOADING is a valid transition.

Second solution requires two HTTP requests and is also not very intuitive and more error prone as the client has to manually setup the relation ship (i.e. it could happen that the client creates the download resource but fails to setup the relationship…). Btw. is there a way to do this in a single HTTP request?

Do you’ve any better idea or recommendation :blush:?

Thanks a lot in advance!


Representing Non-CRUD Actions in JSON API Payloads
#2

Why not

POST /updates/{update_id}/downloads
DELETE /downloads/{download_id}
// or if download_id is unique only inside updates
DELETE /updates/{update_id}/downloads/{download_id}

?


#3

@ksimka Thanks for the quick response. I’m not really sure if it’s valid to directly issue a POST /updates/{update_id}/downloads request to create a new downloads resource object + add it as relationship to the updates resource object. So far as I understood it’s only possible to modify relationships via /updates/<id> or /updates/<id>/relationships/downloads and the downloads resource object which you want to link in must be already existing.

Maybe another variant for creating/starting the download could be

POST /downloads HTTP/1.1
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "data": {
    "type": "downloads",
    "relationships": {
      "update": {
        "data": { "type": "updates", "id": "<id>" }
      }
    }
  }
}

This follows maybe more the idea of “creating a download” resource instead of performing an action like “starting a download”.


#4

As for me, I believe that API stands for “interface”, not “application logic”. So you can do with relationships on server-side what you want.

Your last example looks good too.