“Save,” “submit,” and “remove” sound like clear operations until you try to turn them into API requests. Saving might create a new object, replace existing settings, or change one field. Removing might delete a resource or end a business process while keeping its history.
Mapping operations to HTTP methods means making those intentions precise.
This chapter develops a practical approach using a bookstore's catalog, reservations, account preferences, and reading lists. The goal is a consistent contract between what the caller requests and what the service actually does.
Before choosing a method, describe the operation in terms of its target and intended outcome. “Update the database” is too broad. “Set this account's notification preferences to these values” is specific enough to evaluate.
Use three questions to frame the decision:
The diagram shows how those decisions form an operation contract:
The same business verb can lead to different methods because its target and outcome differ. “Save a new reading list” and “save the complete preferences for my existing account” do not necessarily ask the service to do the same kind of work.
A database statement is not the deciding factor. Obtaining a reservation might insert a hold record, update stock allocations, and enqueue expiry work. The caller still requests one reservation. Internal implementation steps should not force the client to issue separate public operations.
Start with a small map of actual consumer tasks. The following choices belong to this example bookstore API:
This is a design map, not a requirement that every resource offer every method. Catalog entries are read-only for shoppers. Preferences always exist for an account in this model, so the API does not offer a separate creation operation for them.
The pairing matters. POST on /reservations asks the collection to create a hold. PUT on that same collection would express replacement of the target collection's state, not “add one reservation.” This API does not support that operation.
HEAD can accompany a read when metadata without response content is useful. OPTIONS can report supported communication options. Do not use either as a substitute for a missing business operation.
Choose GET for ordinary retrieval. Inspecting availability or an existing reservation should not obtain stock, release a hold, or complete checkout as part of the requested behavior.
An intentionally flawed mapping is GET /reservations/res_731?release=true. It disguises a change as retrieval. A link follower or an automatic retry of a read could then release the customer's stock. Moving the operation to a state-changing method is necessary; renaming the query parameter is insufficient.
For obtaining a hold, the bookstore accepts a submission to the reservations collection. The client supplies the book and quantity. The service chooses the identity, associates the hold with the authenticated customer, and calculates its expiry.
The examples use HTTP/1.1 over HTTPS at https://api.bookstore.example. EXAMPLE_TOKEN is a nonfunctional credential placeholder. Each JSON body occupies one line without a trailing newline.
Assume two copies are available and the service secures them immediately. It returns:
The workflow uses two distinct request targets: the collection receives the creation request, and the resulting item becomes the target for inspection. The diagram makes that distinction explicit:
POST is appropriate because the collection processes a request to obtain a new hold. A known item URL by itself would not justify PUT: the client would also need a contract for supplying that item's desired state.
If allocation is still pending, this example must not return an active reservation. A different design could expose pending work and an acceptance response, but the method alone cannot communicate whether the service has secured the hold. Choose the response around the actual outcome.
POST also fits processing that does not create a lasting object, such as a shipping estimate that requires complex, structured input. Do not create a persistent resource solely to justify POST, and do not force a large input into a GET body. Document what the processing request returns and whether it has business side effects.
For an update, decide what the supplied content means. Is it the desired replacement state of the target, or instructions for modifying its current state?
The bookstore separates notification preferences from the rest of an account. The preferences resource has exactly two writable Boolean fields, order_updates and recommendations. It exists from account creation, and account identity and security settings belong elsewhere.
An owner who explicitly sets both preferences can send a replacement:
Once the replacement succeeds, the response is:
For this PUT contract, the request must include both fields. Omitting one fails validation; it does not silently preserve the old value. The request does not replace the whole account because the target is the smaller preferences resource.
Now consider a screen containing only a recommendations toggle. It should not need to read and resend the order-update preference. This API supports JSON Merge Patch for that partial change:
The successful response is:
Here the patch changes recommendations and leaves order_updates unchanged. The media type supplies the merge-patch interpretation. PATCH alone does not define how a JSON object modifies a resource.
The service validates the resulting preferences. Merge Patch uses null to request removal of an object member; because this schema requires both fields, {"recommendations":null} fails validation. A patch that combines a valid change with an invalid value must fail without applying either change.
The useful distinction is the client's intention, not the number of bytes. A complete representation can be tiny, and a patch can modify many values. If the resource contains too much data for ordinary clients to replace, reconsider what belongs in that resource before treating every PUT as a partial update.
Use DELETE when removing the target resource matches the consumer's intended outcome. The bookstore lets an owner delete a reading list:
After removal, the API responds:
For this contract, subsequent reads return 404, and deleting the already absent list also returns 404. The books the list references remain in the catalog. Removing a list is not a request to delete its books.
Ending a process can have different meaning. The bookstore retains a released reservation so its owner can inspect the outcome. Treating “release this hold” as “delete this reservation” would obscure that retained business record. Define how releasing a reservation changes its state, then choose a state-changing method that fits that behavior.
Similarly, completing checkout can validate a hold, create an order, and trigger fulfillment. A generic PATCH accepting arbitrary status values does not express all those rules by itself. POST can carry a business submission when its processing semantics fit; the API must specify its target, inputs, and effects.
Method selection cannot settle an unclear business operation. First decide whether the client is setting a value, ending a resource, or requesting a process with its own rules.
Once you map operations, record which methods each target actually supports. This prevents a framework's generic handlers from exposing operations the product never intended to allow.
The reservation collection lists only the caller's holds. The item methods in the table let clients inspect reservations. A release operation would need its own documented behavior. Braces indicate a route-template placeholder.
A method unsupported at the target is different from a supported operation that the caller cannot perform. The diagram distinguishes the three questions a design review must answer; it does not prescribe validation order:
For example, an authenticated caller attempts to delete their preferences:
That target does not support deletion:
A 405 requires an Allow header listing supported methods. It does not describe a permission failure.
For supported operations, this example distinguishes these failures:
The authorization example assumes the caller may know the list exists. Private resources require a disclosure policy as well. These status choices communicate the operation's outcome; choosing the correct method does not guarantee success or bypass validation.
Review a proposed mapping against what happens if the same request arrives twice. Setting preferences to the same values should not toggle them on the second attempt. Deleting an absent list should not delete a different resource. Submitting the same reservation request twice might create two holds unless the API provides duplicate-request protection.
PUT and DELETE are idempotent: repeating an identical request has the same intended effect as applying it once. That does not require identical response codes. POST and PATCH do not provide that guarantee solely through their method names.
The recommendation patch in this example assigns a Boolean value, so repeating it has the same effect on that preference. A different patch format could express an increment instead. Review the operation and patch semantics before deciding whether a client may retry automatically.
Do not rename a reservation submission to PUT merely to claim retry safety. A PUT design needs a known target and replacement semantics the service can honor. Duplicate submission handling is a separate part of a POST contract.
Also consider another client editing the resource between requests. A replacement based on an old read can overwrite a newer preference. Method selection does not detect that conflict; the API needs a version precondition or another concurrency policy when such overwrites are unacceptable.
Map an operation by identifying its target, intended change, and observable result. Use retrieval for reads, submission for resource-specific processing, replacement for complete desired state, patches for defined changes, and deletion for removal.
Support only the operations each resource needs. Make validation, permissions, completion, repeat requests, and concurrent changes part of the contract so the method and the service's behavior remain consistent.