HTTP Methods Reference — GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS
In this tutorial, you will learn about HTTP Methods Reference. We cover key concepts, practical examples, and best practices to help you master this topic.
HTTP methods define the action to perform on a resource. GET retrieves, POST creates, PUT replaces, PATCH partially updates, DELETE removes, HEAD checks headers, and OPTIONS discovers available methods.
What You'll Learn
- The semantics of each HTTP method
- Which methods are safe, idempotent, and cacheable
- Typical response codes for each method
Why It Matters
Choosing the wrong HTTP method creates confusion and breaks client expectations. A POST that's treated as idempotent, a DELETE that returns a body, or a PUT that partially updates all violate HTTP semantics and cause integration problems.
Real-World Use
DodaTech's API strictly follows HTTP method semantics: GET requests are safe and cacheable, PUT replaces entire resources, PATCH applies partial updates with JSON Patch, and DELETE returns 204 with no body. This predictability allows automated client generation and CDN Caching.
flowchart TD
GET -->|"Safe, Idempotent, Cacheable"| R["Retrieve Resource"]
HEAD -->|"Safe, Idempotent"| H["Headers Only"]
POST -->|"Not Idempotent"| C["Create Resource"]
PUT -->|"Idempotent"| U["Replace Resource"]
PATCH -->|"Not Idempotent"| P["Partial Update"]
DELETE -->|"Idempotent"| D["Remove Resource"]
OPTIONS -->|"Safe"| O["Discover Methods"]
style GET fill:#bbf7d0,stroke:#16a34a
style POST fill:#fef3c7,stroke:#d97706
style DELETE fill:#fecaca,stroke:#dc2626
GET Implementation
@app.route('/api/products/<int:product_id>', methods=['GET'])
def get_product(product_id):
product = database.get_product(product_id)
if not product:
return jsonify({"error": "not_found", "message": "Product not found"}), 404
response = jsonify(product.to_dict())
response.headers['Cache-Control'] = 'public, max-age=60'
response.headers['ETag'] = compute_etag(product)
return response
POST Implementation
@app.route('/api/products', methods=['POST'])
def create_product():
data = request.get_json()
if not data or 'name' not in data:
return jsonify({"error": "validation_error", "message": "Name is required"}), 400
product = database.create_product(data)
response = jsonify(product.to_dict())
response.status_code = 201
response.headers['Location'] = f"/api/products/{product.id}"
return response
PUT vs PATCH
# PUT: full replacement (idempotent)
@app.route('/api/products/<int:product_id>', methods=['PUT'])
def replace_product(product_id):
data = request.get_json()
existing = database.get_product(product_id)
if not existing:
return jsonify({"error": "not_found"}), 404
# Full replacement - missing fields are set to defaults
product = database.update_product(product_id, data)
return jsonify(product.to_dict())
# PATCH: partial update
@app.route('/api/products/<int:product_id>', methods=['PATCH'])
def update_product(product_id):
data = request.get_json()
if not data:
return jsonify({"error": "no_data"}), 400
# Only update provided fields
product = database.partial_update(product_id, data)
return jsonify(product.to_dict())
DELETE and HEAD
# DELETE: remove resource (returns 204 No Content)
@app.route('/api/products/<int:product_id>', methods=['DELETE'])
def delete_product(product_id):
existing = database.get_product(product_id)
if not existing:
return jsonify({"error": "not_found"}), 404
database.delete_product(product_id)
return '', 204
# HEAD: returns headers only (same as GET but no body)
@app.route('/api/products/<int:product_id>', methods=['HEAD'])
def head_product(product_id):
product = database.get_product(product_id)
if not product:
return '', 404
response = make_response()
response.headers['Content-Type'] = 'application/json'
response.headers['Content-Length'] = estimate_content_length(product)
response.headers['ETag'] = compute_etag(product)
return response
# OPTIONS: discover available methods
@app.route('/api/products/<int:product_id>', methods=['OPTIONS'])
def options_product(product_id):
response = make_response()
response.headers['Allow'] = 'GET, PUT, PATCH, DELETE, HEAD, OPTIONS'
return response
Common Mistakes
1. Using POST for Everything
Some developers use POST for all operations. This breaks cacheability and idempotency. Use GET for retrieval, PUT for replacement, DELETE for removal.
2. Returning a Body with DELETE
DELETE should return 204 No Content. Returning a body with the deleted resource is non-standard.
3. Partial Updates with PUT
PUT replaces the entire resource. A client expecting PUT to partially update will be surprised when omitted fields reset to defaults.
4. Not Returning Location Header on POST
POST should return a Location header pointing to the created resource. Forgetting this forces clients to guess the new resource URI.
5. Using POST for Safe Operations
POST is not safe or idempotent. Do not use POST for simple retrievals. Use GET.
Practice Questions
- Which HTTP methods are safe (don't change state)?
- Which methods are idempotent?
- What status code should a successful GET return?
- What should a successful DELETE return?
- What header should a successful POST include?
Answers
- GET, HEAD, OPTIONS are safe. 2. GET, HEAD, PUT, DELETE are idempotent. 3. 200 OK. 4. 204 No Content. 5. Location header pointing to the created resource.
Challenge
Build an API router that validates HTTP method usage: GET endpoints must be safe (no side effects), POST must return 201 with Location header, PUT must replace the entire resource, PATCH must only update provided fields, and DELETE must return 204.
FAQ
Mini Project
Build an HTTP methods Compliance checker: a Flask API with all standard method implementations, a test suite that verifies each method's semantics (safety, idempotency, response codes, headers), and a CLI tool that tests any API for HTTP method compliance.
What's Next
- Learn about GET design guidelines for safe and cacheable retrieval
- Explore POST design for resource creation
- Continue to PUT vs PATCH with JSON Patch and Merge Patch
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro