Search code examples
resthttpapi-design

Which of these two RESTful API design is better?


I'm designing a set of RESTful API for a concept like "jobs". The model of a job looks like this:

Job: {
  id: 1,
  name: "Brew coffee",
  status: "paused" | "running" | "finished",
  progress: 0.75
}

and following RESTful principles, the client CRUDs jobs via HTTP verbs on /api/jobs. For example, initalizing a new job is

POST /api/jobs
--------------
Request body: {
  name: "Push button"
}
---------------
Response status: 200 OK
Response body: {
  id: 2,
  name: "Cook dinner",
  status: "running",
  progress: 0
}

and accessing this job is

GET /api/jobs/2
--------------
Request body: {}
---------------
Response status: 200 OK
Response body: {
  id: 2,
  name: "Cook dinner",
  status: "running",
  progress: 0.5
}

My question is, how should I design the API for an action like "pause"? Off the top my head I'm consider between two options:

One option is to design it as a PATCH request, and declare directly the end state I want, like so

PATCH /api/jobs/2
--------------
Request body: {
  status: "paused"
}
---------------
Response status: 200 OK
Response body: {
  id: 2,
  name: "Cook dinner",
  status: "paused",
  progress: 0.65
}

The other option is to design it as a POST request, and declare the action I want, like so

POST /api/jobs/2
--------------
Request body: {
  action: "pause"
}
---------------
Response status: 200 OK
Response body: {
  // same as above
}

considering in the future I might implement other operations like "resume", "prioritize", "deprioritize", etc., which of these two options do you think is better? Or is there a even better practice?


Solution

  • My question is, how should I design the API for an action like "pause"?

    How would you design a website to support an action like "pause"?

    You'd probably start with a landing page, that the client could GET. The response you return would probably have a representation of the current state of the job, and would also have links, with semantic annotations that the user would understand. One of these would be for the "pause" action.

    Following the pause link might GET a form, with a number of input fields, semantic annotations for each and probably default values for each field. The client would replace some or all of the values in the input fields, and submit the form.

    That would POST the form data to the endpoint you specify, at which point your implementation would go about invoking the pause side effect, responding with a representation describing the result.

    That's the model to follow. The implementation of your REST-API is an adapter that makes your service appear to be a generic website.

    The key idea here is that the client doesn't need to know in advance the URI to use, or the http method to use, because that information is encoded into the representations of the links provided by the server. The client just needs to recognize and follow the links.

    POST is fine; HTML clients have been using it since HTTP/1.0. PUT and PATCH are also fine, if you want to provide a "remote authoring" interface.

    You may want to review Rest Causistry. Make sure that you go through the comments, which is where the real discussion of REST takes place. It's also useful to review Fielding's Paper Tigers and Hidden Dragons...

    I should also note that the above is not yet fully RESTful, at least how I use the term. All I have done is described the service interfaces, which is no more than any RPC. In order to make it RESTful, I would need to add hypertext to introduce and define the service, describe how to perform the mapping using forms and/or link templates, and provide code to combine the visualizations in useful ways.

    Matt Timmermans' comment has it right; if you are just documenting a bunch of disconnected endpoints that the client navigates on their own, then you aren't doing REST. And in solutions where you don't need to allow the clients and servers to evolve independently, that's also fine.

    REST is intended for long-lived network-based applications that span multiple organizations. If you don’t see a need for the constraints, then don’t use them.

    Following up to the comment:

    it seems that this type of navigation-based API organization is very flexible if we are talking about a human operated client (like browser), but I'm not so sure it is easy to code an automatic client that can adjust changes in the server side. "... add hypertext to introduce and define the service, describe how to perform the mapping using forms and/or link templates ..." sounds all too human-oriented to me.

    I had trouble with this too.

    Nobody is claiming that, with a REST API, the machine consumers will magically be able to understand semantic changes in the API. If we want to support existing clients, then the changes we make on the server have to be done in a backwards compatible way. BUT - and this is the key idea - there's an extra layer of indirection between the semantics and the representation.

    To choose a simple example, in an API with pagination, the consumer needs to understand the semantics of next page; it needs to be able to ask the client for a handle to the next page link. But it doesn't need to know anything about how that link is represented, or the link mechanics, or the URI, or anything like that. The generic browser analog knows those bits, and does all that work for the consumer.

    There is still a protocol that the consumer needs to understand; but that protocol is expressed in links, not in URI.