How does a 'cancel order' operation differ when exposed as an RPC method, a REST state change, a GraphQL mutation or a command message?
answer
- what comes back, and when
- what the caller is coupled to
- accepted is not done
- rejection arrives later as an event
basics
~20 sRPC, REST and GraphQL faces return the outcome in the same exchange - a typed result or error, an HTTP status, a selected payload; a command message returns only acceptance, the outcome arriving later. Each couples the caller to different things.
solid answer
~40 sAs an **RPC** method, `CancelOrder(orderId, reason)` returns the updated order or an error in the same call, and the caller is coupled to that method's name and signature, usually through a generated stub. As a **REST** state change - for example a `POST` to a cancellation sub-resource or a status update on `/orders/8812` - the caller gets an HTTP status and a representation, and depends on URLs, representations and HTTP semantics such as `409 Conflict`. As a **GraphQL** mutation, `cancelOrder(id:)` returns a payload whose fields the caller selects, and errors usually travel in the response body. As a **command message**, the caller learns only that the broker accepted it; the cancellation, or its refusal, arrives later as an event, so the caller depends on the message schema and must handle a late rejection.
go deeper
Recall what each face returns for one cancel: a result or error, an HTTP status, a selected payload, or just an acceptance.
Explain what each caller is coupled to and how a refusal reaches it - typed error, 409 Conflict, body error list, or a later event.
Show the production consequences: late rejections in a message-driven flow, clients that miss GraphQL body errors, and stub breakage when an RPC signature changes.
Argue when one operation deserves two faces - a synchronous one for users, an asynchronous one for bulk work - and what keeping both consistent costs.
## One operation, four faces Take a single business operation: **cancel order 8812, reason "customer request"**. It can sit behind any of four interface styles, and the choice changes what the caller sends, what it gets back, when it knows the outcome, and what it is coupled to. | | RPC method | REST state change | GraphQL mutation | Command message | |---|---|---|---|---| | What is sent | `CancelOrder(orderId, reason)` | a write on the order's URL | `mutation { cancelOrder(id: ...) { ... } }` | a `CancelOrder` command on a channel | | What comes back | result or error, same call | HTTP status plus representation | selected fields plus any errors | acceptance by the broker only | | When the outcome is known | at the end of the call | at the end of the request | at the end of the request | later, from an event or a status query | | Coupled to | method name and signature | URLs, representations, HTTP semantics | schema and the fields selected | message schema and channel name | | Needs the order service up now | yes | yes | yes | no | ## The RPC face The caller invokes a named procedure and blocks until it returns. The contract is the method: its name, argument types, result type and error codes. Generated stubs make the call feel local, and a rejected cancellation - the order has already shipped - comes back as a typed error in the same call (in gRPC, a status such as `FAILED_PRECONDITION`). Changing the operation means changing the method, and every client compiled against it notices. ## The REST face REST has no `cancel` verb, so the operation is expressed as a change to a resource. Common options are a `POST` that creates a cancellation under the order or an update of the order's status; which one fits is a resource-modelling decision of its own. Either way, the caller speaks HTTP: a success status with the new representation, `409 Conflict` when the order's current state forbids the change, `404 Not Found` for an unknown order. A generic HTTP client, a gateway and a log reader all understand that exchange without the API's own tooling. ## The GraphQL face The caller sends a mutation document and **selects the fields it wants back** - perhaps just `status` and `cancelledAt`. Two consequences: - the response shape is the client's choice, so the server can add fields without disturbing anyone; - errors usually come back in the response body's error list alongside any partial data, rather than as a distinct HTTP status per failure, so callers must inspect the body, not just the status line. ## The command-message face The caller publishes a `CancelOrder` command and moves on. The broker's acknowledgement means **accepted, not done**: 1. The order service consumes the command whenever it is running, possibly seconds or hours later. 2. It decides - cancel, or refuse because the parcel has left the warehouse. 3. It publishes the outcome as an event (`OrderCancelled` or a rejection), or records it where the caller can look it up. What the caller gains is **decoupling in time**: the order service can be down while the command is sent. What it loses is the answer. Its user interface must show "cancellation requested" rather than "cancelled", and it must be ready for a refusal that arrives after the user has moved on. Brokers commonly deliver at least once, so a consumer that can see the same command twice must make processing it twice harmless; how that is done belongs to idempotency design rather than to the choice of style. ## How the choice is made - **Who needs the outcome, and how soon?** A customer clicking "cancel" expects a yes or no on screen, which favours a request/response face. A batch job cancelling stale orders overnight does not. - **Who are the callers?** Many unknown HTTP clients favour REST; a few internal services sharing generated code favour RPC; a client assembling a screen from the order, its items and its refund favours GraphQL. - **What must survive the order service being down?** Only the command message does. - **What breaks when the operation changes?** RPC clients break on signature changes; REST clients on URL or representation changes; GraphQL clients mainly when a field they select is removed or changes type, or an argument becomes required; message consumers when the message schema changes incompatibly. A single system often uses more than one face: a synchronous face for the user's click, and a command or event behind it for the slow work.
- If the command-message face is chosen, how does the user learn that the cancellation was refused?Only through a later channel: the order service publishes an outcome event that the caller consumes and turns into a notification, or the caller polls a status resource for the cancellation request. The interface must say 'cancellation requested' until then, because the broker's acknowledgement promised only that the command was accepted, not that the order can still be cancelled.
- Why does a GraphQL caller have to read the response body even when the HTTP status is 200?GraphQL servers usually report field and mutation errors in the body's error list, alongside any data that did resolve, instead of mapping each failure to its own HTTP status. A caller that checks only the status line can treat a refused cancellation as a success.
saying these in an interview costs you the question
- A broker's acknowledgement of a cancel command means the order is cancelled.
- REST cannot express a cancel operation because HTTP has no cancel method.
- A GraphQL mutation failure always returns a non-200 HTTP status.
- Moving cancel to a message queue removes the need to handle a refusal.
- RPC and REST faces differ only in URL spelling, not in what callers depend on.