HTTP Methods: GET, POST & More
HTTP methods (also called verbs) tell the server what kind of operation you want to perform on a resource. Choosing the right method makes your API predictable, maintainable, and aligned with REST conventions.
1. The Complete Set of HTTP Methods
| Method | Purpose | Body? | Safe? | Idempotent? | Cacheable? |
|---|---|---|---|---|---|
GET | Retrieve a resource | No | ✅ | ✅ | ✅ |
HEAD | Retrieve headers only (no body) | No | ✅ | ✅ | ✅ |
POST | Create a new resource / trigger an action | Yes | ❌ | ❌ | Sometimes |
PUT | Replace a resource entirely | Yes | ❌ | ✅ | ❌ |
PATCH | Partially update a resource | Yes | ❌ | ❌ | ❌ |
DELETE | Remove a resource | Optional | ❌ | ✅ | ❌ |
OPTIONS | Ask what methods are supported | No | ✅ | ✅ | ❌ |
CONNECT | Open a tunnel (used for HTTPS proxies) | No | ❌ | ❌ | ❌ |
TRACE | Echo the request (debugging, mostly disabled) | No | ✅ | ✅ | ❌ |
Key Properties Explained
- Safe: Does not alter server state. A safe method should only read data, never write it.
- Idempotent: Calling the same operation multiple times produces the same result as calling it once.
DELETE /users/42twice — the user is still gone after both calls. - Cacheable: The response can be stored and reused for future equivalent requests.
2. GET — Retrieving Data
GET is the most common HTTP method. Every time you visit a web page, your browser sends a GET request.
Rules for GET
- Never change server state — GET must be a pure read
- No request body (browsers ignore it; some servers reject it)
- Always cacheable — browsers and CDNs can store GET responses
- Query parameters go in the URL:
/users?sort=name&page=2
// Simple GET
const response = await fetch('/api/courses');
const courses = await response.json();
// GET with query parameters
const params = new URLSearchParams({ level: 'beginner', sort: 'title', page: '1' });
const response = await fetch(`/api/courses?${params}`);
const courses = await response.json();
// GET a specific resource by ID
const response = await fetch('/api/courses/42');
const course = await response.json();
Raw GET request:
GET /api/courses?level=beginner&page=1 HTTP/1.1
Host: api.nandhoo.com
Accept: application/json
Authorization: Bearer eyJhbG...
Expected response:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: public, max-age=60
[{ "id": 1, "title": "HTML5", ... }, ...]
3. POST — Creating Resources
POST sends data to the server to create a new resource or trigger an action (like sending an email, running a batch job, or logging in).
Rules for POST
- Has a request body with the data to create
- The server decides the new resource's URL (usually returned in the
Locationheader) - Not idempotent — clicking "submit" twice may create two records
- Response is usually
201 Createdwith the new resource in the body
// Create a new user account
const response = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: 'AliceCodes',
email: 'alice@example.com',
password: 'secure-hashed-server-side'
})
});
// 201 Created response includes the new resource
const newUser = await response.json();
console.log('Created:', newUser.id);
console.log('Find at:', response.headers.get('Location')); // /api/users/99
Raw POST request:
POST /api/users HTTP/1.1
Host: api.nandhoo.com
Content-Type: application/json
Content-Length: 68
{"username":"AliceCodes","email":"alice@example.com"}
Expected response:
HTTP/1.1 201 Created
Location: /api/users/99
Content-Type: application/json
{"id":99,"username":"AliceCodes","createdAt":"2025-03-31T07:38:00Z"}
4. PUT — Replacing a Resource Completely
PUT replaces the entire resource at a given URL with the data in the request body. Fields not included in the body are deleted or reset to defaults.
// Replace the entire user profile
const response = await fetch('/api/users/99', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: 'AliceCodes',
email: 'alice-new@example.com',
level: 'intermediate',
bio: 'I love coding!'
// ⚠️ All fields must be provided — omitted fields will be cleared
})
});
If idempotent — sending the same PUT twice results in the same server state.
5. PATCH — Partial Updates
PATCH updates only the specified fields of a resource, leaving everything else unchanged.
// Only update the email — everything else stays the same
const response = await fetch('/api/users/99', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: 'alice-patched@example.com'
// username, level, bio are untouched
})
});
PUT vs PATCH — When to Use Which
| Scenario | Use |
|---|---|
| Replacing a whole document/profile | PUT |
| Updating 1 or 2 fields on a large object | PATCH |
| Unknown which fields changed | PATCH |
| Third-party API must receive complete replacement | PUT |
6. DELETE — Removing a Resource
DELETE removes the resource at the given URL.
// Delete a course by ID
const response = await fetch('/api/courses/42', {
method: 'DELETE',
headers: { 'Authorization': `Bearer ${token}` }
});
if (response.status === 204) {
console.log('Deleted successfully');
} else if (response.status === 404) {
console.log('Course not found — already deleted?');
}
- Response is usually
204 No Content(success, no body) - Or
200 OKwith a confirmation body - Idempotent — deleting something that's already gone returns the same state (though status might be 404)
7. HEAD — Check Before You Download
HEAD is identical to GET, but the server returns only the headers — no body. Use it to:
- Check if a resource exists (without downloading it)
- Get the file size (
Content-Length) before downloading - Check if cached content is still fresh (
ETag,Last-Modified)
// Check if a large file has changed before downloading it
const headRes = await fetch('/downloads/course-data.zip', {
method: 'HEAD'
});
const size = headRes.headers.get('content-length');
const etag = headRes.headers.get('etag');
const changed = etag !== localStorage.getItem('last-etag');
console.log(`File size: ${(size / 1024 / 1024).toFixed(1)} MB`);
console.log(`Changed since last download: ${changed}`);
if (changed) {
// Now download the full file
const fullRes = await fetch('/downloads/course-data.zip');
const blob = await fullRes.blob();
localStorage.setItem('last-etag', etag);
}
8. OPTIONS — CORS Preflight
OPTIONS is used automatically by browsers before certain cross-origin requests (CORS preflight). You rarely call it manually.
// Browser sends this automatically before a cross-origin PUT/DELETE/custom-header request
OPTIONS /api/users/99 HTTP/1.1
Host: api.otherdomain.com
Origin: https://nandhoo.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Authorization, Content-Type
Server must respond with:
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://nandhoo.com
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 86400
9. REST API Design with HTTP Methods
A well-designed REST API uses HTTP methods consistently:
Resource: /api/courses
GET /api/courses → List all courses
POST /api/courses → Create a new course
GET /api/courses/:id → Get a specific course
PUT /api/courses/:id → Replace a course entirely
PATCH /api/courses/:id → Update part of a course
DELETE /api/courses/:id → Delete a course
HEAD /api/courses/:id → Check if a course exists
Nested resources:
GET /api/courses/:id/chapters → List all chapters for a course
POST /api/courses/:id/chapters → Add a chapter to a course
DELETE /api/courses/:id/chapters/:cid → Remove a specific chapter
Method → Status Code Conventions
| Method | Success Response |
|---|---|
GET | 200 OK |
POST | 201 Created + Location header |
PUT / PATCH | 200 OK (with body) or 204 No Content |
DELETE | 204 No Content |
| Any method (not found) | 404 Not Found |
| Any method (wrong method) | 405 Method Not Allowed |
10. Common Mistakes
| Mistake | Problem | Fix |
|---|---|---|
Using GET to delete data (e.g., /delete?id=42) | Search engines can crawl and trigger delete | Use DELETE method |
| Using POST for read operations | Not cacheable, confusing for API consumers | Use GET with query parameters |
| Using PUT when you mean PATCH | Clears fields the client didn't intend to remove | Use PATCH for partial updates |
| Not returning 201 with a Location header on POST | Client has to make another request to find the new resource | Return 201 + Location: /api/resource/new-id |
Ignoring 405 Method Not Allowed | API silently ignores unknown methods | Check allowed methods in the Allow response header |
11. Seeing All Methods In Practice
const API = 'https://api.nandhoo.com';
const headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
};
// 1. GET — fetch all courses
const list = await fetch(`${API}/courses`, { headers }).then(r => r.json());
// 2. POST — create a course
const created = await fetch(`${API}/courses`, {
method: 'POST', headers, body: JSON.stringify({ title: 'HTTP Basics', level: 'beginner' })
}).then(r => r.json());
// 3. PUT — fully replace the course
await fetch(`${API}/courses/${created.id}`, {
method: 'PUT', headers,
body: JSON.stringify({ title: 'HTTP Deep Dive', level: 'intermediate', chapters: 10 })
});
// 4. PATCH — only update the title
await fetch(`${API}/courses/${created.id}`, {
method: 'PATCH', headers,
body: JSON.stringify({ title: 'HTTP Mastery' })
});
// 5. DELETE — remove the course
await fetch(`${API}/courses/${created.id}`, { method: 'DELETE', headers });
12. Review Questions
- What is the difference between a safe and an idempotent method?
- Why should
GETrequests never have side effects? - What is the key difference between
PUTandPATCH? - What status code should a successful
POSTreturn, and what extra header should it include? - When would you use
HEADinstead ofGET? - What happens if you send a
DELETErequest to a URL that no longer exists? - Why is
POSTnot idempotent?