46

In this article the author claims that

Sometimes, it is required to expose an operation in the API that inherently is non RESTful.

and that

If an API has too many actions, then that’s an indication that either it was designed with an RPC viewpoint rather than using RESTful principles, or that the API in question is naturally a better fit for an RPC type model.

This reflects what I have read and heard elsewhere as well.

However I find this quite confusing and I would like to get a better understanding of the matter.

Example I: Shutting down a VM through a REST interface

There are, I think, two fundamentally different ways to model a shutdown of a VM. Each way might have a few variations, but let's concentrate on the most fundamental differences for now.

1. Patch the resource's state property

PATCH /api/virtualmachines/42
Content-Type:application/json  

{ "state": "shutting down" }

(Alternatively, PUT on the sub-resource /api/virtualmachines/42/state.)

The VM will be shutting down in the background and at some later point in time depending of wether shutting down will succeed or not the state might be internally updated with "power off".

2. PUT or POST on the resource's actions property

PUT /api/virtualmachines/42/actions
Content-Type:application/json  

{ "type": "shutdown" }

The result is exactly the same as in the first example. The state will be updated to "shutting down" immediately and maybe eventually to "power off".

Are both designs RESTful?

Which design is better?

Example II: CQRS

What if we have a CQRS domain with many such "actions" (aka commands) that might potentially lead to updates of multiple aggregates or cannot be mapped to CRUD operations on concrete resources and sub-resources?

Should we try to model as many commands as concrete creates or updates on concrete resources, where ever possible (following the first approach from example I) and use "action endpoints" for the rest?

Or should we map all the commands to action endpoints (as in the second approach of example I)?

Where should we draw the line? When does the design become less RESTful?

Is a CQRS model a better fit for an RPC like API?

According to the quoted text above it is, as I understand it.

As you can see from my many questions, I am a little bit confused about this topic. Can you help me to get a better understanding of it?

2
  • "creating an action" does not seem RESTful, except if the executed action has its own resource identifier afterwards. Otherwise changing the "state" property via PATCH or PUT makes more sense. For the CQRS part, I don't have a good answer yet. Commented Dec 22, 2016 at 12:05
  • 3
    @Laiv Nothing wrong with that. It's an academical question, I'd like to get a deeper understanding of the matter. Commented Dec 22, 2016 at 14:10

5 Answers 5

34

In the first case (shut-down of VMs), I'd consider none of the OP alternatives RESTful. Granted, if you use the Richardson maturity model as a yardstick, they are both leve 2 APIs because they use resources and verbs.

Neither of them, though, use hypermedia controls, and in my opinion, that's the only type of REST that differentiates RESTful API design from RPC. In other words, stick with level 1 and 2, and you're going to have an RPC-style API in most cases.

In order to model two different ways of shutting down a VM, I'd expose the VM itself as a resource that (among other things) contains links:

{
    "links": [{
        "rel": "shut-down",
        "href": "/vms/1234/fdaIX"
    }, {
        "rel": "power-off",
        "href": "/vms/1234/CHTY91"
    }],
    "name": "Ploeh",
    "started": "2016-08-21T12:34:23Z"
}

If a client wishes to shut down the Ploeh VM, it ought to follow the link with the shut-down relationship type. (Normally, as outlined in the RESTful Web Services Cookbook, you'd use an IRI or more elaborate identification scheme for relationship types, but I chose to keep the example as simple as possible.)

In this case, there's little other information to provide with the action, so the client should simple make an empty POST against the URL in the href:

POST /vms/1234/fdaIX HTTP/1.1

(Since this request has no body, it'd be tempting to model this as a GET request, but GET requests should have no observable side-effects, so POST is more correct.)

Likewise, if a client wants to power off the VM, it'll follow the power-off link instead.

In other words, the relationship types of the links provide affordances that indicate intent. Each relationship type has a specific semantic significance. This is the reason we sometimes talk about the semantic web.

In order to keep the example as clear as possible, I intentionally obscured the URLs in each link. When the hosting server receives the incoming request, it'd know that fdaIX means shut down, and CHTY91 means power off.

Normally, I'd just encode the action in the URL itself, so that the URLs would be /vms/1234/shut-down and /vms/1234/power-off, but when teaching, that blurs the distinction between relationship types (semantics) and URLs (implementation details).

Depending on which clients you have, you may consider making RESTful URLs non-hackable.

CQRS

When it comes to CQRS, one of the few things that Greg Young and Udi Dahan agrees about is that CQRS isn't a top-level architecture. Thus, I'd be cautious about making a RESTful API too CQRS-like, because that'd mean that clients become part of your architecture.

Often, the driving force behind a real (level 3) RESTful API is that you want to be able to evolve your API without breaking clients, and without having control of clients. If that's your motivation, then CQRS wouldn't be my first choice.

21
  • 5
    @leifbattermann They're not RESTful because they use the message body to communicate intent; that's clearly RPC. If you used links to arrive at those resources, then why would you need to communicate intent through the body? Commented Dec 22, 2016 at 13:25
  • 2
    @guillaume31 DELETE seems strange to me because after shutting down the vm will still exist, only in state "power off" (or sth. like that). Commented Dec 22, 2016 at 14:07
  • 1
    The resource doesn't have to reflect an VM atemporally, it can represent an execution instance of it. Commented Dec 22, 2016 at 14:09
  • 1
    @Maggyero You can subject resources to different actions via HTTP verbs. The intent behind a GET request is (hopefully) distinct from the intent behind a DELETE request. Usually, in REST, that's sufficient to distinguish between intent. The OP is a little special because it asks how to distinguish between two kinds of intent that don't easily map to two distinct HTTP verbs, which is why it's necessary to model the two distinct intents as two distinguishable resources. The RESTful Web Services Cookbook calls these Controller resources. Commented May 17, 2021 at 5:40
  • 1
    @Maggyero By default, I use href and rel as originally defined by ATOM. Again, I just went with Subbu Allamaraju's recommendations. Here's an example: blog.ploeh.dk/2020/10/26/fit-urls Commented May 17, 2021 at 17:03
11

Shutting down a VM through a REST interface

This is actually a somewhat famous example, put forth by Tim Bray in 2009.

Roy Fielding, discussing the problem, shared this observation:

I personally prefer systems that treat monitored state (like power status) as non-editable.

In short, you have one information resource that returns a current representation of the monitored state; that representation might include a hypermedia link to a form that to request a change to that state, and the form has another link to a resource to handle (each) change request.

Seth Ladd had the key insights in the problem

We've turned Running from a simple state of a person to a true Noun which can be created, updated, and talked about.

Taking this back to rebooting machines. I would argue that you would POST to /vdc/434/cluster/4894/server/4343/reboots Once you've posted, you have a URI which represents this reboot, and you can GET it for status updates. Through the magic of hyperlinking, the representation of the Reboot is linked to the Server that is rebooted.

I think minting URI space is cheap, and URI's are even cheaper. Create a collection of activities, modeled as Nouns, and POST, PUT, and DELETE away!

RESTful programming is Vogon bureaucracy at web scale. How do you do anything RESTful? Invent new paperwork for it, and digitize the paperwork.

In somewhat fancier language, what you are doing is defining the domain application protocol for "shut down a VM", and identifying the resources that you need to expose/implement that protocol

Looking at your own examples

PATCH /api/virtualmachines/42
Content-Type:application/json  

{ "state": "shutting down" }

That's OK; you aren't really treating the request itself as its own separate information resource, but you could still manage.

You've missed a little bit in your representation of the change.

With PATCH, however, the enclosed entity contains a set of instructions describing how a resource currently residing on the origin server should be modified to produce a new version.

For example, the JSON Patch media type formats instructions as though you were directly modifying a JSON document

[
    { "op": "replace", "path": "state", "value": "shutting down" }
]

In your alternative, the idea is close, but not obviously correct. PUT is a complete replacement of the state of the resource at the target URL, so you probably wouldn't choose a spelling that looks like a collection as the target of a representation of a single entity.

POST /api/virtualmachines/42/actions

Is consistent with the fiction that we are appending an action to a queue

PUT /api/virtualmachines/42/latestAction

Is consistent with the fiction that we are making an update to the tail item in the queue; it's a bit weird to do it this way. Principle of least surprise recommends giving each PUT it's own unique identifier, rather than putting them all to one place and modifying multiple resources at the same time.

Note that, in so far as we are discussing the spelling of URI -- REST doesn't care; /cc719e3a-c772-48ee-b0e6-09b4e7abbf8b is a perfectly cromulent URI as far as REST is concerned. Readability, as with variable names, is a separate concern. Using spellings that are consistent with RFC 3986 will make people a lot happier.

CQRS

What if we have a CQRS domain with many such "actions" (aka commands) that might potentially lead to updates of multiple aggregates or cannot be mapped to CRUD operations on concrete resources and sub-resources?

Greg Young on CQRS

CQRS is a very simple pattern that enables many opportunities for architecture that may otherwise not exist. CQRS is not eventual consistency, it is not eventing, it is not messaging, it is not having separated models for reading and writing, nor is it using event sourcing.

When most people talk about CQRS they are really speaking about applying the CQRS pattern to the object that represents the service boundary of the application.

Given that you are talking about CQRS in the context of HTTP/REST, it seems reasonable to assume you are working in this latter context, so let's go with that.

This one, surprisingly, is even easier than your previous example. The reason for this is simple: commands are messages.

Jim Webber describes HTTP as the application protocol of a 1950s office; work gets done by taking messages and putting them into inboxes. Same idea holds - we get a blank copy of a form, fill it out with the specifics that we know, deliver it. Ta da

Should we try to model as many commands as concrete creates or updates on concrete resources, where ever possible (following the first approach from example I) and use "action endpoints" for the rest?

Yes, insofar as the "concrete resources" are messages, rather than entities in the domain model.

Key idea: your REST API is still an interface; you should be able to change the underlying model without clients needing to change the messages. When you release a new model, you release a new version of your web endpoints that know how to take your domain protocol and apply it to the new model.

Is a CQRS model a better fit for an RPC like API?

Not really -- in particular, web caches are a great example of an "eventually consistent read model". Making each of your views independently addressable, each with their own caching rules, gives you a bunch of scaling for free. There's relatively little appeal to an exclusively RPC approach to reads.

For writes, it's a tricker question: sending all commands to a single handler at a single endpoint, or a single family of endpoints, is certainly easier. REST is really more about how you find communicate where the endpoint is to the client.

Treating a message as its own unique resource has the advantage that you can use PUT, alerting the intermediary components to the fact that the handling of the message is idempotent, so that they can participate in certain cases of error handling, is a nice to have. (Note: that from the point of view of the clients, if the resources have different URI, then they are different resources; the fact that they may all have the same request handler code on the origin server is an implementation detail hidden by the uniform interface).

Fielding (2008)

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.

3

Are both designs RESTful?

So if you want to think restfully then forget about commands. The client doesn't tell the server to shut down the VM. The client "shuts dow" (metaphorically speaking) their copy of the resource representation by updating its state and then PUTs that representation back on the server. The server accepts that new state representation and as a side-effect of this, actually shuts down the VM. The side-effect aspect is left up to the server.

It is less of

Hey server, client here, would you mind shutting down the VM

and more of

Hey server, client here, I updated the state of resource VM 42 into the shutdown state, update your copy of this resource and then do what ever you think is appropriate

As a side effect of the server accepting this new state it can check what actions it has to actually perform (such as physically shutting down VM 42), but this is transparent to the client. The client is unconcerned with any actions the server has to take to become consistent with this new state

So forget about commands. The only commands are the verbs in HTTP for state transfer. The client doesn't know, nor does it care, how the server is going to get the physical VM into the state of shut down. The client isn't issuing commands to the server to achieve this, it is just saying This is the new state, figure it out.

The power of this is that it decouples the client from the server in terms of flow control. If later the server changes how it works with VMs, the client doesn't care. There are no command endpoints to update. In RPC if you change the API end point from shutdown to shut-down you have broken all your clients as they now don't know the command to call on the server.

REST is similar to declarative programming, where instead of listing a set of instructions to change something you instead just state how you want it to be and let the programming environment figure it.

26
  • 1
    It's better to use POST /api/virtualmachines/42/shutdown instead of having any "side effect". The API must be understandable to the user, how will I know that for example DELETE /api/virtualmachines/42 will shut the VM down? A side effect to me is a bug, we should design our APIs to be understandable and self-descriptive
    – Konrad
    Commented Aug 28, 2018 at 7:19
  • 1
    That is just remote procedure masquerading as REST/HTTP. If you are dead set on RPC you are better off just using a RPC protocol. The URL scheme is not "the API". The URL scheme says where the resources are. /api/virtualmachine/42/shutdown is not a resource. The state transfer way of doing this is to have a resource representation of Virtual Machine 42 that the client updates the state to "shutdown" and sends that state via a PUT to the server. The server then says "oh the state of the virtual machine should now be shutdown, let me do that". The client doesn't worry about how it does this Commented Aug 29, 2018 at 8:08
  • 1
    Taking the bank account example, the client doesn't tell the server to "deposit" or "withdraw". The client updates the balance of the account (essentially doing a withdraw or a deposit itself) and then sends the new state to the server. The server either accepts this new state or rejects it. This is actually a good example of the advantages of REST. The client does not need to know that "deposit" is the command to add money and "withdraw" is the command to delete money. It just changes the amount of the bank account to what it wants and sends that new state to the server. Commented Aug 29, 2018 at 9:50
  • 1
    @CormacMulhall ‘Sure you can POST the state change to the actual resource you are changing but why would you bother, just use PUT.’ As I said, you should use PUT for replacing the current state of a target resource with a new state, because the request can be repeated in case of network failure since PUT is an idempotent method. But you should use POST if you don’t know the target resource or the new state. This is what Fielding means when he says: ‘POST only becomes an issue when it is used in a situation for which some other method is ideally suited’.
    – Géry Ogam
    Commented Jun 22, 2021 at 18:20
  • 1
    @CormacMulhall … For example, the payload of a 200 (OK) response to GET (Section 4.3.1) represents the current state of the target resource, as observed at the time of the message origination date (Section 7.1.1.2), whereas the payload of the same status code in a response to POST might represent either the processing result or the new state of the target resource after applying the processing. Response messages with an error status code usually contain a payload that represents the error condition, such that it describes the error state and what next steps are suggested for resolving it.’
    – Géry Ogam
    Commented Jun 22, 2021 at 18:20
2

The example in the linked article is predicated on the idea that starting the machine and shutting it down have to be directed by commands instead of by changes in the state of modeled resources. The latter is pretty much what REST lives and breathes. Better modeling of the VM requires a look at how its real-world counterpart works and how you, as a human, would interact with it. This is long-winded, but I think it gives good insight into the type of thinking required to do good modeling.

There are two ways to control the power state of the computer on my desk:

  • Power Switch: Immediately cuts the flow of electricity to the power supply, bringing the entire computer to a sudden, disorderly halt.
  • On/Off Button: Tells the hardware to notify the software that something on the outside wants everything shut down. The software does an orderly shutdown, notifies the hardware that it's done and the hardware signals the power supply that it can go into its standby state. If the power switch is on, the machine is running and the software is in a state where it can't respond to the shutdown signal, the system won't shut down unless I turn off the power switch. (A VM will behave in exactly the same way; if the shutdown signal is ignored by the software, the machine will continue running I have to force it to power off.) If I want to be able to restart the machine, I have to turn the power switch back on and then press the on/off button. (Many computers have the option of using a long press of the power button to go directly to the standby state, but this model doesn't really need that.) This button can be treated like a toggle switch because pressing it results in different behavior depending on the state when it's pressed. If the power switch is off, pressing this button does absolutely nothing.

For a VM, both of these can be modeled as read/write Boolean values:

  • power - When changed to true, nothing happens other than a note being made that the switch has been placed into this state. When changed to false, the VM is commanded into an immediate power-off state. For the sake of completeness, if the value is unchanged after a write, nothing happens.

  • onoff - When changed to true, nothing happens if power is false, otherwise the VM is commanded to start. When changed to false, nothing happens if power is false, otherwise, the VM is commanded to notify the software to do an orderly shutdown, which it will do and then notify the VM that it can go into the power-off state. Again, for completeness, a no-change write does nothing.

With all of this comes the realization that there's one situation when the state of the machine doesn't reflect the state of the switches, and that's during shutdown. power will still be true and onoff will be false, but the processor is still running its shutdown, and for that we need to add another resource so we can tell what the machine is actually doing:

  • running - A read-only value that's true when the VM is running and false when it isn't, determined by asking the hypervisor for its state.

The upshot of this is that if you want a VM to start up, you have to make sure the power and onoff resources have been set true. (You could allow the power step to be skipped by making it self-resetting, so that if set to false, it becomes true after the VM has been verifiably hard-stopped. Whether that's a RESTfully-pure thing to do is fodder for another discussion.) If you want it to do an orderly shutdown, you set onoff to false and come back later to see if the machine actually stopped, setting power to false if it didn't.

Like the real world, you still have the problem of being directed to start the VM after it's had onoff changed to false but is still running because it's in the middle of shutdown. How you deal with that is a policy decision.

2

You could leverage 5 levels of media type to specify the command in the content-type header field of the request.

In the VM example, it would be something along these lines

PUT /api/virtualmachines/42
Content-Type:application/json;domain-model=PowerOnVm

> HTTP/1.1 201 Created
Location: /api/virtualmachines/42/instance

Then

DELETE /api/virtualmachines/42/instance
Content-Type:application/json;domain-model=ShutDownVm

Or

DELETE /api/virtualmachines/42/instance
Content-Type:application/json;domain-model=PowerOffVm

See https://www.infoq.com/articles/rest-api-on-cqrs

1
  • Be advised, 5LMT was a proposed solution and is not supported by standards. I came across the CQRS article before and learned much from it.
    – Peter L
    Commented Jul 14, 2019 at 16:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.