Chapter 24: AJAX Request
1. The big picture: What does “sending a request to a server” mean in AJAX?
When we say AJAX sends a request to a server, we mean:
JavaScript code running inside the browser quietly opens a network connection to a server (the same server that served the page, or a different one), asks for something or sends something, waits for an answer, and then uses that answer to update the current page without reloading.
The most important thing to understand first:
The browser is not reloading the page. It is making a separate HTTP request in the background — exactly like when you type a URL in a new tab, but invisible to the user.
2. Three main modern ways to send a request in 2025–2026
| Method | How old? | Style today | Ease of use | Promise-based? | Still used directly? | Recommendation 2025–2026 |
|---|---|---|---|---|---|---|
| XMLHttpRequest (XHR) | 1999–2005 | Classic / old-school | Medium | No | Yes (legacy code) | Understand it, but avoid |
| fetch | 2015–2017 | Modern standard | High | Yes | Very widely used | Primary choice |
| axios (library) | ~2014 | Very popular helper | Very high | Yes | Very common | Excellent for most projects |
Today most developers use fetch or axios. But you still need to understand XMLHttpRequest because:
- Many old tutorials use it
- Many existing codebases still use it
- Some very specific behaviors (upload progress, older CORS tricks) are easier with XHR
3. Step-by-step: Sending a request with XMLHttpRequest (classic way)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>AJAX – Sending Request with XMLHttpRequest</title> <style> body { font-family: Arial, sans-serif; margin: 40px; line-height: 1.6; } button { padding: 12px 24px; font-size: 16px; margin: 0 10px 10px 0; cursor: pointer; } #status { margin: 20px 0; padding: 15px; background: #f8f9fa; border: 1px solid #ccc; border-radius: 6px; min-height: 100px; } pre { background: #f4f4f4; padding: 12px; border-radius: 4px; overflow-x: auto; } </style> </head> <body> <h1>AJAX – Sending a Request to a Server</h1> <p>Watch the status messages carefully — notice the page never reloads.</p> <button onclick="getHello()">1. Simple GET – Hello</button> <button onclick="getTime()">2. GET – Server time</button> <button onclick="postLogin()">3. POST – Fake login</button> <button onclick="getError()">4. GET – Force 404 error</button> <div id="status">Click any button above to see what happens…</div> <script> const statusDiv = document.getElementById('status'); function show(message, isError = false) { statusDiv.innerHTML = ` <strong style="color: ${isError ? 'darkred' : 'darkgreen'}"> ${isError ? 'ERROR' : 'SUCCESS'} </strong><br><br> ${message.replace(/\n/g, '<br>')} `; } function sendRequest(method, url, body = null, headers = {}) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open(method, url, true); // true = asynchronous // Add custom headers if any for (const [key, value] of Object.entries(headers)) { xhr.setRequestHeader(key, value); } xhr.onload = function() { if (xhr.status >= 200 && xhr.status < 300) { resolve({ status: xhr.status, statusText: xhr.statusText, responseText: xhr.responseText, responseJSON: xhr.responseType === 'json' ? xhr.response : null }); } else { reject({ status: xhr.status, statusText: xhr.statusText, responseText: xhr.responseText }); } }; xhr.onerror = function() { reject({ status: 0, statusText: 'Network Error', responseText: 'Cannot connect to server' }); }; if (body) { xhr.send(body); } else { xhr.send(); } }); } // ──────────────────────────────────────────────── // Example 1: Simple GET request // ──────────────────────────────────────────────── async function getHello() { try { show("Sending request…"); const result = await sendRequest("GET", "https://httpbin.org/anything/hello"); show(`Received from server:\n\n{result.responseText}`); } catch (err) { show(`Request failed:\nStatus: ${err.status}\n${err.statusText || err.responseText}`, true); } } // ──────────────────────────────────────────────── // Example 2: GET – asking for current server time // ──────────────────────────────────────────────── async function getTime() { try { show("Asking server for current time…"); const result = await sendRequest("GET", "https://httpbin.org/anything/time"); show(`Server says:\n\n${result.responseText}`); } catch (err) { show(`Error:\n${err.status} ${err.statusText}`, true); } } // ──────────────────────────────────────────────── // Example 3: POST – sending data (fake login) // ──────────────────────────────────────────────── async function postLogin() { const payload = { username: "john_doe", password: "secret123" }; try { show("Sending login credentials…"); const result = await sendRequest( "POST", "https://httpbin.org/post", JSON.stringify(payload), { "Content-Type": "application/json" } ); const responseData = JSON.parse(result.responseText); show(` Login attempt received by server.<br><br> Server echoed back:<br> <pre>${JSON.stringify(responseData.json, null, 2)}</pre> `); } catch (err) { show(`Login failed:\n${err.status} ${err.statusText}`, true); } } // ──────────────────────────────────────────────── // Example 4: Forcing an error (404) // ──────────────────────────────────────────────── async function getError() { try { show("Trying to access a non-existing resource…"); const result = await sendRequest("GET", "https://httpbin.org/status/404"); show("This should not appear", false); } catch (err) { show(` Expected error occurred:<br><br> Status: ${err.status}<br> Message: ${err.statusText}<br> ${err.responseText ? `<pre>${err.responseText}</pre>` : ''} `, true); } } </script> </body> </html> |
6. What you should notice while testing
- No page reload — only the small box changes
- Network tab (F12 → Network) shows a separate request
- Status codes are visible (200 = OK, 404 = not found…)
- POST sends body (you can see it in network tab)
- Errors are handled gracefully
7. Quick Summary – Sending a Request with XMLHttpRequest
| Action | Code pattern |
|---|---|
| Create object | new XMLHttpRequest() |
| Set method & URL | xhr.open(“GET”/”POST”, url, true) |
| Add headers (JSON, auth…) | xhr.setRequestHeader(“Content-Type”, “application/json”) |
| Send GET | xhr.send() |
| Send POST / PUT | xhr.send(JSON.stringify(data)) |
| Handle success | xhr.onload = … → check xhr.status 200–299 |
| Handle network error | xhr.onerror = … |
8. Modern recommendation (2025–2026)
Use fetch instead for new code — it’s cleaner, Promise-based, and built-in:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username: "john", password: "123" }) }) .then(r => r.json()) .then(data => console.log(data)) .catch(err => console.error(err)); |
But knowing XMLHttpRequest is still valuable because:
- You’ll find it in legacy code
- Some tutorials still use it
- Certain features (fine-grained upload progress, older IE compatibility) are easier with XHR
Would you like to continue with one of these next?
- Full POST form submission with XMLHttpRequest
- File upload with progress bar using XHR
- How to automatically parse JSON with XHR
- Comparison table: XMLHttpRequest vs fetch vs axios
- Handling CORS, timeout, abort
- Converting old XHR code to modern fetch
Just tell me what you want to dive deeper into! 😊
