HTTP Methods: GET, POST & More

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

MethodPurposeBody?Safe?Idempotent?Cacheable?
GETRetrieve a resourceNo
HEADRetrieve headers only (no body)No
POSTCreate a new resource / trigger an actionYesSometimes
PUTReplace a resource entirelyYes
PATCHPartially update a resourceYes
DELETERemove a resourceOptional
OPTIONSAsk what methods are supportedNo
CONNECTOpen a tunnel (used for HTTPS proxies)No
TRACEEcho 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/42 twice — 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 Location header)
  • Not idempotent — clicking "submit" twice may create two records
  • Response is usually 201 Created with 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

ScenarioUse
Replacing a whole document/profilePUT
Updating 1 or 2 fields on a large objectPATCH
Unknown which fields changedPATCH
Third-party API must receive complete replacementPUT

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 OK with 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

MethodSuccess Response
GET200 OK
POST201 Created + Location header
PUT / PATCH200 OK (with body) or 204 No Content
DELETE204 No Content
Any method (not found)404 Not Found
Any method (wrong method)405 Method Not Allowed

10. Common Mistakes

MistakeProblemFix
Using GET to delete data (e.g., /delete?id=42)Search engines can crawl and trigger deleteUse DELETE method
Using POST for read operationsNot cacheable, confusing for API consumersUse GET with query parameters
Using PUT when you mean PATCHClears fields the client didn't intend to removeUse PATCH for partial updates
Not returning 201 with a Location header on POSTClient has to make another request to find the new resourceReturn 201 + Location: /api/resource/new-id
Ignoring 405 Method Not AllowedAPI silently ignores unknown methodsCheck 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

  1. What is the difference between a safe and an idempotent method?
  2. Why should GET requests never have side effects?
  3. What is the key difference between PUT and PATCH?
  4. What status code should a successful POST return, and what extra header should it include?
  5. When would you use HEAD instead of GET?
  6. What happens if you send a DELETE request to a URL that no longer exists?
  7. Why is POST not idempotent?