I'm trying to convert a set of SOAP-based services to a RESTful API.
I started by identifying resources by analyzing operation names and I got the resource Subscription
.
When I need to update the state of the subscription, I cannot simply send a POST
request to the server, because I don't have direct access to the resources, but I need to call some RPC-style operations to update their properties. Additionally, only and only if I'm changing the state of the subscription to "active", an additional call to an external service is required.
In these cases, what is the best practice to handle underlying operations?
The solution I came up with is to use query parameters, so that if I need to call activation service, I can use something like:
POST /subscriptions/{subscriptionid}/?activate=true
Considering that I cannot directly update my Subscription object fields, is there any best practice to handle this kind of conversion?
Update 1:
I can put in the body of my POST request some values, for instance "state":"active"
and check inside my service the proper operations to be triggered.
-
REST's mapping of commands to HTTP verbs fails with complex operations. You are better off just doing a RPC style call POST activateSubscription/{id} no-one will be confused by itEwan– Ewan2016年06月28日 09:30:05 +00:00Commented Jun 28, 2016 at 9:30
-
@Ewan I'm not sure this complies with the RESTful model, but I came up with another solution: in my code I can call the proper RPC-style operation according to the input payload (I can pass state=active in the body of my post request, the code will call the activation code)Vektor88– Vektor882016年06月28日 09:52:13 +00:00Commented Jun 28, 2016 at 9:52
-
1An update to an existing resource like this should be a PATCH, and the body of the query is then a partial model of what you are changing. A POST is supposed to be a request that creates a resource. This distinction, apart from being clearer to the user, will make it easier for your code to know when this operation is happening rather than a resource post.Mr Cochese– Mr Cochese2016年06月28日 10:42:59 +00:00Commented Jun 28, 2016 at 10:42
-
1@Vektor88 Typically, but those are idempotent operations where you need to pass in the entire resource state representation. This use case seems far more like a partial update, which fits a PATCH really well.Mr Cochese– Mr Cochese2016年06月28日 10:56:16 +00:00Commented Jun 28, 2016 at 10:56
-
1@MrCochese POST is not idempotent.JimmyJames– JimmyJames2016年06月28日 14:55:01 +00:00Commented Jun 28, 2016 at 14:55
3 Answers 3
You need to watch this talk by Jim Webber.
When I need to update the state of the subscription, I cannot simply send a POST request to the server, because I don't have direct access to the resources, but I need to call some RPC-style operations to update their properties. Additionally, only and only if I'm changing the state of the subscription to "active", an additional call to an external service is required.
Think "messaging"; send a message to your domain, describing what you want to have happen. The side effect of the message is that your domain model actually changes its state. The "resource" is the message queue.
POST /subscriptions/{subscriptionid}/?activate=true
The spelling of the resource name doesn't matter to the machines; but people tend to get fussy when the identifiers you use break from the convention that resources are "nouns".
Also, we're talking about a resource that is subordinate to /subscriptions/{subscriptionid}
, so convention (see RFC 3986) calls for expressing that relationship with a path segment, rather than using the query part.
So these spellings might be reasonable
POST /subscriptions/{subscriptionid}/messages
POST /subscriptions/{subscriptionid}/activations
-
1Jim Webber's talk is available at youtube.com/watch?v=aQVSzMV8DWcuser674669– user6746692018年08月17日 22:34:37 +00:00Commented Aug 17, 2018 at 22:34
REST is non-functional. Activate
is a verb and can not be a state, Active
is a state.
Because RESTful is non-functional you can not tell a RESTful service what to do, but you can add work for the queue of a service.
See this:
PUT /subscriptionQueue
subscriptionId={subscriptionId}
active=true
This request is RESTful and supports all benefits of RESTful (like performance, acid...)
-
How about this: PUT /subscriptionQueue/subscriptions/{subscriptionId} { active=true } Sorry, no linebreaks in comments.Christoph– Christoph2021年03月28日 18:28:32 +00:00Commented Mar 28, 2021 at 18:28
-
@Christoph Yes, that might even be better than my example. "subscriptionQueue/subscriptions" might confuse because the noun repeats but it is totally fine IMO.Grim– Grim2021年03月29日 06:12:45 +00:00Commented Mar 29, 2021 at 6:12
-
1"How about this: PUT /subscriptionQueue/subscriptions/{subscriptionId} { active=true } Hm, linebreaks work!Grim– Grim2021年11月02日 05:43:42 +00:00Commented Nov 2, 2021 at 5:43
If its a boolean flag to activate/deactivate stuff, i'd say the default is to use JSON:
POST /subscriptions/{subscriptionid}/
{
format: 0,
subscription:
{
active: false
}
}
This is easily extended if you want to support more properties. Another approach is giving it its own endpoint:
POST /subscriptions/{subscriptionid}/active/
DELETE /subscriptions/{subscriptionid}/active/
Personally, i would only use this if the active
state of this event needs/has properties that you then can pass/get in JSON, like an user ID or setting.
If its not a boolean value but just an action that you need to trigger but dont need/have any status feedback for (except an immediate 200 OK), i'd use an endpoint like this to trigger it much like a RPC:
POST /subscriptions/{subscriptionid}/activate/
When in doubt, read this: http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api#restful (see "What about actions that don't fit into the world of CRUD operations?")
Explore related questions
See similar questions with these tags.