REST endpoint: how to proper design an action on a resource?

2.4k Views Asked by At

I have the resource /contracts with the following structure:

{
    name: "Contract name",
    signedAt: 123213231,
    // etc.
}

While basic CRUD operations are well clear (GET /contracts, GET /contracts/{id}, POST /contracts, etc.) some doubts come when I have to do some concrete actions on the resource.

One of these actions is the following:

  • sign: means the contract is signed, so the signedAt date will need to be updated with the moment (date-time) the contract was signed.

So far I've been thinking about these different approaches:

PATCH-ing the resource

This approach will mean having the following endpoint method:

PATCH /contracts/{id}

and just posting the signedAt date { signedAt: 123213231 } meaning that after this the contract will be signed.

What I don't like about this approach is that the signature date comes from the client, I was thinking that having this date initialized on the backend side whenever a contract is signed could be better and more consistent.

Totally discarded, as the signedAt date should be set on the server side exactly at the moment the sign is done.

POST-ing a new resource

This approach will mean having the signature action as a resource:

POST /contracts/{id}/sign

with an empty body in this case as we don't need to pass anything else so, once it is posted, the backend side would be the responsible for having the signature date initialized.

POST-ing the resource using 'action'

Similar to the previous approach, in this case I would use a query parameter called action on the contract resource:

POST /contracts/{idContract}?action=sign

also with an empty body where ?action=sign. Like in the previous approach, once posted the backend side would be the responsible for having the signature date initialized.

Questions

  • What would be the proper way to have this designed at a REST API level?
  • Is any of the approaches above close to a good design or not?
  • Would I need to modify any of the approaches?
  • Is there any better alternative?
2

There are 2 best solutions below

9
Veselin Davidov On

I have designed a few rest APIs myself but I am not a restful evangelist so my answer might not be the best. I would suggest some of the following:

  • Create a custom converter for date values in your rest service that accepts date AND other specific fields. If you checkGoogle reporting APIs for example they allow you to use specific date range and also CURRENT_WEEK, CURRENT_MONTH etc. So you can add such specific value and use it. For example PATCH signedAt=CURRENT_DATE and the API handles that properly.

  • Add a boolean signed field to the resource. Do a POST or PATCH with signed=true. This way you will be able to eventually query only signed resources easily ;) Also it might be the case that people care only about if it is signed than when it was signed

I wouldn't use ?action=sign or /contracts/{id}/sign because these are not RESTFUL and even if you do use GET and POST you would use them in a way to create a workaround in order to implement actions in your design which shouldn't have actions

0
VoiceOfUnreason On

just posting the signedAt date { signedAt: 123213231 } meaning that after this the contract will be signed.

On HTTP Patch

The set of changes is represented in a format called a "patch document" identified by a media type.

Rather than rolling your own bespoke media type, you might want to consider whether one of the standard formats is suitable.

For example: JSON Patch.

Content-Type: application/json-patch+json

[ { "op": "replace", "path": "signedAt", "value": 123213231 } 

JSON Merge Patch is another reasonable option

Content-Type: application/merge-patch+json

{ signedAt: 123213231 } 

From what I can see, the primary difference is that JSON Patch provides a test operation which gives you finer grain control than simply relying upon validators

But you are absolutely right - PATCH gives the client code authority to specify the time value. If that's not appropriate for your use case, then PATCH is the wrong tool in the box.

POST /contracts/{id}/sign
POST /contracts/{idContract}?action=sign

As far as REST/HTTP are concerned, these two choices are equivalent -- you are updating the state of one resource by sending an unsafe request to a different resource. There are some mild differences in how these spellings act when resolving references, but as request-targets, it doesn't make a difference to the client.

An option that you seem to have overlooked:

POST /contracts/{id}

action=sign

This has the advantage that, when successful, you get cache invalidation for free.

In a hypermedia API, the flow might go something like this: the client would GET the resource; because the resource hasn't been signed yet, the representation could include a form, with a "sign" button on it. The action on the form would be /contracts/{id}. The consumer "signs" the contract by submitting the form -- the agent gathers up the information described by the form, encodes it into the request body, and then posts the request to the server. The server responds success, and the client's cache knows to invalidate the previously fetched copy of the resource.