Definition
- GET – retrieves a representation of a resource, is safe (never changes state) and idempotent, and its responses are cacheable.
- POST – creates a new resource under a collection or triggers a process, is neither safe nor idempotent, and returns
201 Createdwith aLocationheader. - PUT – fully replaces a resource at a known URL, is idempotent, and missing fields are cleared because the body represents the whole resource.
- PATCH – partially updates a resource by sending only the changed fields, and is not guaranteed idempotent.
- DELETE – removes a resource and is idempotent, typically returning
204 No Content. - HEAD – returns only the status and headers of a GET response with no body, used to check existence or size.
Imagine National Bank of India's records room. GET is asking to see Rajesh's file without changing anything. POST is opening a new transfer slip, creating a fresh record. PUT is replacing Priya's entire profile form with a new one. PATCH is changing only Priya's phone number and leaving the rest. DELETE is shredding a beneficiary record. HEAD is asking whether Rajesh's file exists and how big it is, without pulling the whole file. OPTIONS is asking which actions are allowed on this file.
Understanding the Concepts
What are the HTTP methods and their roles?
HTTP methods, also called verbs, tell the server what action to perform on a resource identified by a URL. In RESTful APIs, each method has defined semantics around safety, idempotency, and cacheability that clients, caches, and proxies depend on.
- Safe means the method does not change server state. GET, HEAD, and OPTIONS are safe.
- Idempotent means calling it once or many times leaves the server in the same final state. GET, HEAD, OPTIONS, PUT, and DELETE are idempotent.
// Summary of HTTP methods in a banking REST API:
// GET /api/v1/accounts/SAV2001 → Read Rajesh's account (safe, idempotent)
// POST /api/v1/transfers → Create a new transfer (not safe, not idempotent)
// PUT /api/v1/beneficiaries/BEN-77 → Replace beneficiary entirely (idempotent)
// PATCH /api/v1/beneficiaries/BEN-77 → Update nickname only (not guaranteed idempotent)
// DELETE /api/v1/beneficiaries/BEN-77 → Remove beneficiary (idempotent)
// HEAD /api/v1/statements/2026-09 → Check if statement exists (safe, idempotent)
// OPTIONS /api/v1/transfers → Which methods are allowed? (safe, idempotent)
How does GET work?
GET retrieves a representation of a resource and must be safe and idempotent. It never changes server state, so calling GET /api/v1/accounts/SAV2001 a hundred times returns Rajesh's account a hundred times and changes nothing. GET responses can be cached by browsers, CDNs, and proxies.
package com.bank.controller;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/v1/accounts")
public class AccountController {
@GetMapping("/{id}")
public AccountResponse getAccount(@PathVariable String id) {
return accountService.findById(id); // safe: no state change
}
}
How does POST work?
POST creates a new resource under a collection URL or triggers a process. It is neither safe nor idempotent: two identical POSTs usually create two resources. The server returns 201 Created with a Location header pointing to the new resource.
package com.bank.controller;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import jakarta.validation.Valid;
import java.net.URI;
@RestController
@RequestMapping("/api/v1/transfers")
public class TransferController {
@PostMapping
public ResponseEntity<TransferResponse> create(@Valid @RequestBody TransferRequest req) {
TransferResponse resp = transferService.create(req);
URI location = URI.create("/api/v1/transfers/" + resp.id());
return ResponseEntity.created(location).body(resp); // 201 + Location header
}
}
How do PUT and PATCH differ?
PUT fully replaces a resource at a known URL. The body is the entire new representation, so any field the client omits is cleared or set to default. PUT is idempotent because sending the same full body twice leaves the resource in the same state.
PATCH applies a partial modification. The client sends only the fields to change, and everything else stays. PATCH is not guaranteed idempotent because a PATCH like "add 100 to the balance" would stack on repeats.
// PUT: full replacement. Omitting ifsc means ifsc becomes null.
@PutMapping("/beneficiaries/{id}")
public Beneficiary replace(@PathVariable String id,
@RequestBody BeneficiaryRequest body) {
return beneficiaryService.replace(id, body); // replaces all fields
}
// PATCH: partial update. Only non-null fields are applied.
@PatchMapping("/beneficiaries/{id}")
public Beneficiary patch(@PathVariable String id,
@RequestBody PatchBeneficiaryRequest body) {
return beneficiaryService.patch(id, body); // changes only sent fields
}
How does DELETE work?
DELETE removes a resource. It is idempotent: deleting an already-deleted resource still leaves it deleted. The common pattern returns 204 No Content with an empty body.
@DeleteMapping("/beneficiaries/{id}")
public ResponseEntity<Void> delete(@PathVariable String id) {
beneficiaryService.delete(id);
return ResponseEntity.noContent().build(); // 204 No Content
}
What are HEAD and OPTIONS used for?
HEAD behaves exactly like GET but the server returns only the status line and headers, with no body. It is used to check existence, file size via Content-Length, or freshness via ETag without downloading the payload.
OPTIONS asks the server which methods are allowed on a resource. The response carries an Allow header. It is also the mechanism behind CORS preflight requests from browsers.
// HEAD: check if a statement exists and its size without downloading.
// HEAD /api/v1/statements/2026-09
// Response: 200 OK, Content-Length: 2048576, no body
// OPTIONS: which methods are allowed?
// OPTIONS /api/v1/transfers
// Response: 200 OK, Allow: GET, POST
What is the safety and idempotency contract?
The properties of safe and idempotent are contracts the whole HTTP ecosystem depends on. A proxy may freely retry a GET or PUT after a network timeout because repeating them is harmless. But it must not blindly retry a POST because that could create a duplicate transfer and move Rajesh's money twice.
| Method | Safe | Idempotent | Cacheable | Typical status |
|---|---|---|---|---|
| GET | Yes | Yes | Yes | 200 OK |
| POST | No | No | No | 201 Created |
| PUT | No | Yes | No | 200 or 204 |
| PATCH | No | No | No | 200 OK |
| DELETE | No | Yes | No | 204 No Content |
| HEAD | Yes | Yes | Yes | 200 OK (no body) |
| OPTIONS | Yes | Yes | No | 200 OK |
// A proxy retries GET safely after a timeout – no side effects.
// A proxy must NOT retry POST – it could create a duplicate transfer.
// To make POST safe to retry, use an Idempotency-Key header.
@PostMapping("/transfers")
public ResponseEntity<TransferResponse> create(
@RequestHeader("Idempotency-Key") String key,
@Valid @RequestBody TransferRequest req) {
return ResponseEntity.ok(transferService.createOnce(key, req));
}
What are common mistakes with HTTP methods?
The most common mistake is using GET for operations that change state, like GET /api/v1/accounts/SAV2001/close. This is dangerous because a browser prefetch, a search engine crawler, or a CDN may issue a GET automatically and close the account. Another mistake is using POST for everything, turning the API into RPC.
// Wrong: GET that changes state. A prefetch could close the account.
@GetMapping("/accounts/{id}/close")
public String closeAccount(@PathVariable String id) {
accountService.close(id); // Dangerous: GET must be safe
return "closed";
}
// Correct: POST or DELETE for state-changing operations.
@PostMapping("/accounts/{id}/close")
public ResponseEntity<Void> closeAccount(@PathVariable String id) {
accountService.close(id);
return ResponseEntity.noContent().build();
}
When should you use each method?
Use GET to read a resource or list a collection. Use POST to create a new resource or trigger an action that is not idempotent. Use PUT when the client sends the complete replacement. Use PATCH when the client sends only the fields to change. Use DELETE to remove a resource. Use HEAD to check existence or metadata cheaply. Use OPTIONS for CORS preflight or capability discovery.
// Complete CRUD for accounts:
// GET /accounts → list all accounts
// GET /accounts/SAV2001 → read one account
// POST /accounts → create a new account
// PUT /accounts/SAV2001 → replace the account entirely
// PATCH /accounts/SAV2001 → update specific fields
// DELETE /accounts/SAV2001 → close/remove the account
// HEAD /accounts/SAV2001 → check existence
What should a senior engineer know about HTTP methods?
A senior engineer knows that choosing the correct verb directly affects retry safety, caching, and intermediary behaviour. They know that POST is the only non-idempotent verb among the commonly used ones, and that payment APIs retrofit idempotency onto POST using an Idempotency-Key header because moving money twice is unacceptable. They also know about content negotiation using the Accept header and the produces attribute in Spring.
Visual Overview
┌───────────────────────────────────────────────────────┐ │ HTTP Methods in REST │ ├───────────────────────────────────────────────────────┤ │ │ │ Safe (read-only) State-changing │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ GET │ │ POST │ │ │ │ HEAD │ │ PUT │ │ │ │ OPTIONS │ │ PATCH │ │ │ └──────────────┘ │ DELETE │ │ │ └──────────────┘ │ │ │ │ Idempotent (safe to retry) │ │ ┌─────────────────────────────────────┐ │ │ │ GET HEAD OPTIONS PUT DELETE │ │ │ └─────────────────────────────────────┘ │ │ │ │ NOT idempotent (retry may duplicate) │ │ ┌─────────────────────┐ │ │ │ POST PATCH │ │ │ └─────────────────────┘ │ │ │ │ Request Flow: │ │ Client ──GET /accounts/SAV2001──▶ Server │ │ ◀── 200 OK + JSON body ─── │ │ │ │ Client ──POST /transfers──▶ Server │ │ ◀── 201 Created + Location ─── │ │ │ │ Client ──DELETE /beneficiaries/BEN-77──▶ Server │ │ ◀── 204 No Content ─── │ └───────────────────────────────────────────────────────┘
Key Takeaway
In a RESTful API every HTTP method has a defined role and defined properties around safety and idempotency. GET reads a resource and is both safe and idempotent, meaning it never changes server state and can be called repeatedly with the same result, which also makes it cacheable. POST creates a new resource under a collection and is neither safe nor idempotent, so two identical POSTs normally create two resources, which is why the server returns 201 Created with a Location header.
PUT and PATCH are both updates but they mean different things. PUT is a full replacement and is idempotent, so if a client omits a field, PUT semantics say that field is cleared, because PUT means the body is the entire resource now. PATCH is a partial update where the client sends only the fields to change. In the beneficiary work at National Bank of India, when Priya wanted to change only the nickname of her landlord beneficiary, PATCH was the right verb, because sending a PUT with a missing account number would have wiped out that field.
DELETE removes a resource and is idempotent, returning 204 No Content. HEAD returns only headers with no body, used to check existence cheaply. OPTIONS reports which methods a resource supports through the Allow header and handles CORS preflight.
The reason these properties matter beyond theory is retry behaviour. A proxy or client may safely retry a GET, PUT, or DELETE after a network timeout because repeating them is harmless. But it must never blindly retry a POST, because that could create a duplicate transfer and move Rajesh's money twice. So choosing the correct verb is not cosmetic: it directly protects correctness when the network is unreliable, which in a banking system is exactly where correctness matters most.
Remember one sentence: each HTTP method carries a contract about safety, idempotency, and cacheability that the entire ecosystem of clients, proxies, and caches depends on.
---