5

Let's imagine that I have an employee resource.

{
   "id": 1,
   "name": "John",
   "role": "Office manager"
}

And I want an endpoint that promotes them. As part of that I want some side effects to happen, such as an email goes to HR.

How should I do this in a "restful" manner? Would the following be acceptable? And the email sent as part of this request.

PATCH /employee/1
{
   "role": "senior office manager"
}
  • 2
    Just use a POST for things like this. You're not manipulating a hypermedia "document," so using PATCH makes no sense. What you're attempting is more like RPC. So use a POST. – Robert Harvey Oct 22 '19 at 23:19
  • According to MDN here - developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH - "The HTTP PATCH request method applies partial modifications to a resource" and "PATCH (like PUT) may have side-effects on other resources". It seems to be correct choice for your case. – Rumen Georgiev Nov 22 '19 at 12:49
  • Solve by making "Employee Promotion" a record – cja Jul 19 at 13:04
1

You came upon an operation for which RESTful design simply has not been made. It's a general problem with trying to make an API completely RESTful, and RESTful development should be taken with a grain of salt and pragmatism.

REST and RESTful approaches are good starting points to make your API more predictable. But as evolution goes, you are likely, just like you just have, to encounter a situation in which representing something as a resource does not make a total sense.

You basically have two options:

Follow RESTful practices and forcefully make everything as resource (a noun)

In such case, the best would be to make a new endpoint, POST: /employee/(id)/promotion.

Transform to a more RPC-like approach, where not everything is a noun

This would be preferable by me, introducing a new endpoint, POST: /employee/(id)/promote.


Know that while current problem might be resolved by noun-ification of the operation, this won't likely be possible for all changes to the system, therefore loosening the rules for your API resource design in most cases proves to be a better, more flexible and easier to work-with approach.

| improve this answer | |
  • 1
    What is different between the two endpoints? Both are a resource as seen from REST. – Euphoric Nov 22 '19 at 9:50
  • @Euphoric, sorry, I meant noun. Fixed. :) – Andy Nov 22 '19 at 9:53
  • 1
    What does it matter what you call it? It has exactly the same semantics and behavior. – Euphoric Nov 22 '19 at 10:00
  • @Euphoric, a noun might make sense in this case, for different cases it might not be as easy. E.g. having some process with which can be started. With nouns, the endpoint /processes/(id)/start would indicate you're adding to the process its start, which is cumbersome, because in reality you're starting the process by executing an operation on it. Another example: accept/reject (short, clear, common) vs acceptance/rejection (longer, who uses this in an actual speech?) of invitations. "I am adding an acceptance to your invitation, therefore accepting it"? Nobody talks like that. – Andy Nov 22 '19 at 10:29
  • @Andy I'm sympathetic to your general idea, but all of those examples can be improved by thinking a bit more broadly about what nouns to use. For instance, rather than /processes/(id)/start, you might POST to /processes/(id)/status, which then gives you a consistent way to read the status with a GET on the same resource. Similarly, the noun involved in a promotion is not the promotion itself but the employee's job title. – IMSoP Nov 23 '19 at 16:11
1

When talking about "resources" in an API, it's tempting to think of the entities you represent in other parts of an application - the objects in your domain layer, or the tables in your database - but that's not necessarily the best abstraction. The common problem is that you end up with a small number of entities, each of which have a large number of possible actions.

In this case, you have chosen "employee" as the resource, and grouped everything under a single "update employee" action. This would require a complex set of validation and business logic to handle all the different combinations - e.g. if the request indicates a new name and a new role, etc.

In an RPC-style API, you would instead have a number of "verbs" or "methods" hanging off this resource, such as "promote employee" and "change employee name". This leads to a different problem, where you have a long list of methods with no clear structure between them.

An alternative design is to group these actions onto smaller resources which have individual state (the S in REST). So "employee" might be a resource, but "employee's current role" would also be a resource in its own right, which would group the actions related to hiring and promotions as changes in state. For instance, one API user might be able to update the role entity to {"role": "senior office manager", "state": "pending_approval"}, and a different user approve the promotion by updating the state to approved or probation.

Ideally, the representation of the "role" resource should include the available actions for the current user as "hypertext links", e.g. if the current state is pending_approval, and the user has the appropriate permission, the representation could include a link of type approve-change. A client could then look for this link type to determine where to submit the next request, without having to hard-code the URL. It could even look up a "hyper-schema" to generate the request body.


I've deliberately avoided describing the design in terms of URLs and HTTP methods, because it's too easy to get tied into implementation details and lose sight of the overall aim of the architecture, but below is an example of what this might look like. This isn't a fully worked-out design - for instance, you might want to separate the current role and requested role somehow, and it probably wouldn't just be a text field - but hopefully demonstrates how a RESTful service could represent the business logic.

GET /employee/1

{
   "id": 1,
   "name": "John",
   "role": "Office Manager",
   "links": {
      "role-details": {"href":"/employee/1/role", "method":"GET"}
   }
}
GET /employee/1/role

{
   "role": "Office Manager",
   "state": "confirmed",
   "links": {
      "request-promotion": {"href":"/employee/1/role", "method":"PUT"}
   }
}
PUT /employee/1/role
{
   "role": "Senior Office Manager"
}

{
   "role": "Senior Office Manager",
   "state": "pending_approval",
   "links": {
      "approve-promotion": {"href":"/employee/1/role", "method":"PUT"}
   }
}
| improve this answer | |
  • So, you downvote my post by suggesting standardized ways of using link-relations (either registered link-relation names or Web linking extensions) and then use i.e. approve-promotion, a custom-one, yourself? This sounds a bit like a hypocrite to me in all honesty. How does your custom one, that is taken out of thin air, differ from one defined by Web linking extensions? Your PUT suggestion is also questionable in regards to RFC 7231 – Roman Vottner Nov 23 '19 at 18:32
  • @RomanVottner I didn't downvote your answer, that was someone else; nor did I say it was wrong, just that it could be clearer about how to apply the principles to the concrete scenario in the question. Regarding this answer, what standard link relationship would you use in place of the custom one (which you're quite right, I just invented off the top of my head)? And could you clarify what you mean about PUT - are you suggesting that since it's not replacing all the fields, it should be a PATCH? – IMSoP Nov 23 '19 at 18:40
0

Using the PATCH method is probably not wrong in the strict sense, but it feels a bit odd for this use.

If I were to design this, I would probably go for a POST request, like this

POST /employee/1/role

"Senior office manager"

With the email being sent as a result of this request.

| improve this answer | |
  • 1
    POST would imply that you are adding a new role to a collection, but generally you will be replacing a single role with another, so PUT would be more appropriate. – IMSoP Nov 23 '19 at 16:11
0

I've once seen suggestion of turning operations on RESTful resources into their own "resources" and POSTing to them to execute them. Something like :

POST /employee/1/Promote
{
   "role": "senior office manager"
}

But I feel that your solution of PATCHing the resource is viable in situations where there are no pre-conditions and the change is always possible.

| improve this answer | |
  • Although this is certainly a valid API design, it's really an RPC design rather than RESTful, because the endpoint is describing the action, not any particular resource or state. – IMSoP Nov 23 '19 at 16:13
-1

As REST is just a generalization of the interaction concept used in the browsable Web, the problem definition can be transformed to "How would you attempt something like that on the Web" and then use the concepts applied for the Web and translate it to the application domain.

On the Web typical CRUD operations are usually achived with a mixture of links, Web forms and images that express some meanings and affordances to us humans. I.e. an edit action usually is depicted with a pencil symbol that upon clicking will render a pre-filled form representation that sends the updated content after pressing a further submit button to the server. Due to the limitations of HTML forms such requests are usually issued as HTTP POST operations.

The affordance now expresses what can or should be done with certain items.

Affordance is a property or feature of an object which presents a prompt on what can be done with this object. In short, affordances are cues which give a hint how users may interact with something, no matter physical or digital (Source)

Mike Amundsen and Asbjørn Ulsberg also explained this concept. Such affordance can either stem from things typical in the real-world, i.e. if you want to remove something from your belongings you throw it into a waste bin as such a waste bin symbol may express a removal, or from domain-specific knowledge, certain standards or ontologies. Think of how pagination is usually achived on the Web. Here you usually have symbols like <<, <, > or >> that express the actions of paging one or multiple pages forwards or backwards.

As REST targets at applications rather than humans, who easily can sense the meaning and affordance of things, a computer will prefer some less ambigious approaches, such as standardized relation types, domain specific knowlege, ontologies or even microformats. Such relations usually make use of Web linking extensions and may also be specified in media-type format descriptions. For paginations i.e. IANA defines prev, next, first and last instead of relying on certain icons that may look different from Web page to Web page. Such link relations howeve are not only used for navigation, they are also used to give clients a hint on when certain resources may be requested. Think of HTTP/2 PUSH including prefetch annotated links or HTTP Preload instructions, while they may behave similar they are though a bit different (Further read).

A promotion of an employee can be seen as an update to the employee's state and as such, as mentioned before, a form-like representation might be used that offers a client choices it can select from. A link offering such a promotion may be annotated with edit-form http://api.acme.com/relation/promotion, where edit-form hints a client that upon invoking that link a form representation will be returned and the custom Web link http://api.acme.com/relation/promotion hints the client about the special purpose of that resource. It might not really know what this URI expresses, and the URI itself does not need to expose a documentation on the purpose itself, the link-relation is just an arbitrary string to the client after all, though it might add special support for such link-relations in future or in a dedicated media-type.

Exposing an own edit-form is especially convenient if there is not a strict hierarchical structure and the employee can be promoted to a totally different position, which also does happen in practice. A promotion might also only be salary-wise or by adding certain benefits. As such allowing one admissible person (or automated service) to alter the configuration is for sure a future-proof way.

In recent years there have been a couple of approaches on form-like representation formats. The probably best known is for sure HTML with its forms definition, though a couple of JSON based approaches such as HAL-Forms, halform or ion evolved, though they are still more or less in draft-phase and not final, at least they currently lack widespread support.

Note that this post so far didn't talk about the actual form of the URI, as this is not relevant in a REST architecture as clients shouldn't attempt extracting knowledge from URIs as the server is free to change its URI-scheme at any time. A client extracting knowledge from URIs directly is highly likely to break when a server chagnes its URI scheme and that is usually a bad thing in remote computing for services that are meant to stay around for years to come and evolve in future. As such I don't agree with the other proposals here so far that more or less promote the usage of a certain URI in combination with a HTTP method. We humans also don't want to read the URI and prefer an accompanying, human-readable text that summarizes the linked content.

How should I do this in a "restful" manner?

REST is all about decoupling clients from servers to allow servers to evolve freely in future and make clients more robust to changes and using a server-teaches-client-what-it-needs-approach. One therefore should use the Web as an example on how to model the interaction in a REST architecture. Fielding made things like HATEOAS (the usage of links and forms) as well as caching, statelessnes and other properties a constraint and not an option. Therefore you will only benefit from a REST architecture if you adhere to these constraints stringent.

As such, the introduction of a dedicated edit form to alter the state of the employee does make sense and allows updates in the future. The form itself is usually exposed as an own resource, that is cacheable, and will target the actual resoure, which upon submitting the data via an unsafe method (may be defined by the media-type, as HTML does, or via a property on the element itself) will evict any cached representations for that resource so that a consecutive GET request is able to fetch the new data instead of any outdated cached data. This is tightly related to caches using the de-facto URI as key for caching response content and evicing those stored data if an unsafe request is performed on that URI.

By supporting multiple form-representation formats you just increase the likelihood that an arbitrary client is able to interact with your service as clients and server should make use of content-type negotiation anyways to increase interoperability chances.

By using link-relations you can hint a client on the purpose of a certain link and let a client decide whether that link is of interest at the current state the client is in. This basically allows the server to change the URI scheme any time it wants to but allows clients to still know the purpose of the URI by just looking at the link-relation.

| improve this answer | |
  • There's a lot of interesting background here, but some of it seems a bit off-topic (e.g. you spend a lot of time talking about HTML etc, but most API clients aren't really like web browsers) and it's hard to see how to actually apply this theory to the question at hand. Your advice also seems a bit contradictory: you recommend indicating relationships with hypertext links rather than URLs, but then suggest a generic link-type of edit-form associated to a URL containing the word promote. I'm not clear how the client would make use of such a link. – IMSoP Nov 23 '19 at 16:51
  • @IMSoP link-relations behave similar to triples in semantic web in that they set a subject in relation with an other resource and add a predicate to that relation. The relation between two things can often be named in different ways, hence multiple predicates may exist for the same relation. Similar to Web linking extensions, computers might not make sense of the triple directly, though through collecting a lot of those triples a whole ontology graph may be spawned an AI algorithm may work upon and conclude next steps. – Roman Vottner Nov 23 '19 at 17:50
  • @IMSoP You further should NOT confuse a Web linking extension for a URI used to target a resource. These are two different things! Further, not edit-form is associated with the promote Web linking extension but the link is associated with both terms. A client that knows what a edit-form link relation is but not what a promote link relation extension means will still be able to decide whether the form is of interest to it or not. The same holds true for the reverse case IMO – Roman Vottner Nov 23 '19 at 17:56
  • @IMSoP ... but most API clients aren't really like web browsers and this is the problem. Most so called "REST APIs" have nothing to do with REST at all. They are RPC at its heart and misuse the term REST for marketing purposes. They don't care for future evolution and will release a whole new API which they squeeze into v2, v3, ..., vN after a couple of years until the blame REST to not deliver what it actually promised. This is though not the failure of REST but the failure of the ones reluctant to grasp what REST is and how to attempt it. – Roman Vottner Nov 23 '19 at 18:01
  • I understand what link relationships are, but I'm not clear why edit-form is a useful example here, or why you would implement an API where it pointed at a URL with the more specific name of promote. Hypertext/HATEOAS is an important concept, but comparisons to web browsers explain it very badly, because web browsers are used directly by humans, and API clients are not. The idea of AI "learning" an API is frankly fantasy, and outside of auto-generated back office systems, most clients of an API are trying to achieve a specific task, and need to "hard code" some information about that task. – IMSoP Nov 23 '19 at 18:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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