xzhu
xzhu

Reputation: 5755

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?

Upvotes: 3

Views: 584

Answers (2)

VoiceOfUnreason
VoiceOfUnreason

Reputation: 57249

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.

Upvotes: 2

JayKuri
JayKuri

Reputation: 849

There are a few aspects of this question, and the option to choose depends on which things you want to set as a priority.

Question 1) Which is better from a REST perspective?

From a strictly REST perspective, the PATCH with { "status": "pause" } is the correct one. REST is REpresentative State Transfer. And this solution most strictly conforms to that.

Question 2) Should I use 'action' oriented endpoints?

From a strictly REST perspective, the answer would be no. That said, however, in practice there are many very good reasons to do this. One of the side effects of strictly REST API implementations is that your API can start to look and act like a web-accessible data store, rather than a self-contained service. What happens when this occurs is usually that more and more of your logic migrates into the client side of the API... with complex actions becoming implemented in the client code as multiple calls to different REST actions.

This is usually not what you want, and while it can be avoided with a carefully crafted API, it most often is not.

Question 3) What is the best practice?

There isn't one, at least not objectively. Different APIs with different purposes fit REST or action oriented calling mechanisms better. That said, I've very rarely encountered an API that was entirely RESTful and had no action oriented routines... and most of them drift that way over time anyway... if for no other reason than most APIs require some sort of search function, and strict REST has no provision for that.

So, in the end, my advice is, use REST where it works, it will make your API easier for other people to get started with... and use action oriented endpoints when the action doesn't fit into the REST concepts well. It's better to have an easy to understand action endpoint than a hard to grasp REST action that triggers magical things behind the scenes.

But that's just like... my opinion, man.

Upvotes: 1

Related Questions