Card 06/ 11

ConceptDifficulty: Intermediate2 min

Actions That Are Not CRUD: Cancelling, Approving and Sending

Cancelling an order is not deleting it, and an API can model it as a resource of its own.

Some operations are not create, read, update or delete: cancelling an order, approving an invoice, sending an email. An API can model them in three ways: as a change to a state field with PATCH, as a new sub-resource created with POST, or, as a last resort, as a path that names the action. Each keeps the record and says what happened, which DELETE would not.

Picture a team arguing over whether DELETE /orders/42 should cancel the order. Finance needs the cancelled order kept for its records, and support needs to see when and why it was cancelled. Deleting erases exactly what both need.

Cancelling order 42 modelled as a state change and as a sub-resource

PATCH /orders/42a state field

Body {"status": "cancelled"}. Simple, and fits when cancelling is just a value change. Rules about which states may follow which must be enforced by the server, and there is nowhere natural for a reason or a refund.

POST /orders/42/cancellationa resource of its own

Body {"reason": "customer request"}. Creates a cancellation with its own data, time and author, readable later at GET /orders/42/cancellation. Fits when the action has details or side effects.

The third way is a controller path. Some APIs use a verb-like segment, such as POST /orders/42:cancel in Google's API style or POST /orders/42/cancel. It is honest about being an action and suits operations with no sensible noun, such as POST /documents/9:translate. Use POST for it, never GET, because it changes state.

How to choose. If the action only moves a status, PATCH is enough. If it carries data of its own, such as a reason, a refund amount or an approver, or if other systems will want to read it later, make it a sub-resource. Reach for a controller path only when neither fits.

What not to do. Do not use DELETE for something that keeps the record, and do not put the action in a GET link, which crawlers and prefetchers will follow.

So ask what the action produces: a new state, a new record, or neither; that answer picks the model.