Chapter 25: AJAX Response
AJAX – Server Response, written as if I’m sitting next to you — explaining everything slowly, clearly, and step by step like a patient teacher who wants you to really understand what happens when the server answers, how to handle different kinds of responses, what can go wrong, and how real developers deal with it in 2025–2026.
We will go very slowly and thoroughly.
1. What actually happens when the server responds?
When you send an AJAX request (whether with XMLHttpRequest, fetch, or any library), the browser does the following behind the scenes:
- Sends the HTTP request
- Waits for the server to reply
- Receives the response which consists of several important parts:
| Part of the response | What it contains | How you access it in code (XHR) | How you access it in fetch |
|---|---|---|---|
| Status code | 200, 404, 500, 403… | xhr.status | response.status |
| Status text | “OK”, “Not Found”, “Internal Server Error”… | xhr.statusText | response.statusText |
| Response headers | Content-Type, Cache-Control, Set-Cookie… | xhr.getResponseHeader(“Content-Type”) | response.headers.get(“Content-Type”) |
| Response body | The actual data (JSON, XML, text, HTML…) | xhr.responseText or xhr.response | response.text() / .json() / .blob()… |
| Response type | How the body was interpreted | xhr.responseType | Determined by which method you call |
The most important thing to understand first:
The server response is not automatically usable. You must check if it was successful and parse it correctly.
2. The three most important questions you must always ask
When the server responds, good AJAX code always asks these three questions:
- Did the network connection work? → onerror / catch / response.ok
- Did the server understand and accept the request? → status between 200–299
- What kind of data did the server send back? → Look at Content-Type header and parse accordingly
3. Realistic Example – Handling Server Response (classic XMLHttpRequest style)
|
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 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>AJAX – Understanding Server Response</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: 30px 0; padding: 20px; background: #f8f9fa; border: 1px solid #ccc; border-radius: 6px; min-height: 140px; white-space: pre-wrap; } .success { color: darkgreen; font-weight: bold; } .error { color: darkred; font-weight: bold; } </style> </head> <body> <h1>AJAX – Understanding the Server Response</h1> <p>Click the buttons and watch how we handle different kinds of responses.</p> <button onclick="testSuccess()">1. Normal success (200)</button> <button onclick="testJson()">2. JSON response</button> <button onclick="testNotFound()">3. 404 Not Found</button> <button onclick="testServerError()">4. 500 Server Error</button> <button onclick="testNoInternet()">5. Simulate no internet</button> <div id="status">Click any button to see how the response is handled…</div> <script> const statusDiv = document.getElementById('status'); function show(message, type = 'info') { let color = '#333'; if (type === 'success') color = 'darkgreen'; if (type === 'error') color = 'darkred'; statusDiv.innerHTML = ` <span style="color:{color}; font-weight:bold;"> [${type.toUpperCase()}] </span><br><br> ${message.replace(/\n/g, '<br>')} `; } function sendRequest(url, expectJson = false) { const xhr = new XMLHttpRequest(); xhr.open("GET", url, true); // Optional: tell XHR we expect JSON if (expectJson) { xhr.responseType = "json"; } xhr.onload = function() { // Step 1: Check if request completed successfully if (xhr.readyState !== 4) return; // Step 2: Check HTTP status const statusClass = (xhr.status >= 200 && xhr.status < 300) ? 'success' : 'error'; let content = ` Status: ${xhr.status} ${xhr.statusText}<br> Content-Type: ${xhr.getResponseHeader("Content-Type") || "unknown"}<br><br> `; // Step 3: Handle different response types if (xhr.status >= 200 && xhr.status < 300) { if (expectJson && xhr.response) { // We asked for JSON and got it parsed content += "Parsed JSON:<br><pre>" + JSON.stringify(xhr.response, null, 2) + "</pre>"; } else { // Raw text response content += "Response body:<br><pre>" + xhr.responseText.substring(0, 800) + (xhr.responseText.length > 800 ? "..." : "") + "</pre>"; } show(content, 'success'); } else { // Server error content += "Error details:<br><pre>" + (xhr.responseText || "No response body") + "</pre>"; show(content, 'error'); } }; xhr.onerror = function() { show("Network error: Cannot reach server (offline, blocked, wrong URL…)", 'error'); }; xhr.send(); } function testSuccess() { sendRequest("https://httpbin.org/anything/success-test"); } function testJson() { sendRequest("https://httpbin.org/json", true); // expect JSON } function testNotFound() { sendRequest("https://httpbin.org/status/404"); } function testServerError() { sendRequest("https://httpbin.org/status/500"); } // Simulate network failure (wrong protocol or invalid host) function testNoInternet() { sendRequest("http://invalid-localhost-999999.com"); } </script> </body> </html> |
4. What you should carefully observe
- Status 200–299 → success path
- Status 400–599 → server understood the request but had a problem
- Status 0 → network failure (offline, CORS block, aborted, invalid URL…)
- Content-Type header tells you how to interpret the body
- application/json → should use .json() or responseType = “json”
- text/xml or application/xml → responseXML
- text/plain, text/html → responseText
- responseType changes how the body is delivered
5. Modern way – Using fetch (what you should prefer today)
|
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 |
async function fetchExample(url, expectJson = false) { try { const response = await fetch(url); // 1. Check network success if (!response.ok) { const text = await response.text(); throw new Error(`Server error {response.status} ${response.statusText}\n${text}`); } // 2. Get the body in the right format let data; const contentType = response.headers.get("content-type") || ""; if (expectJson || contentType.includes("application/json")) { data = await response.json(); } else { data = await response.text(); } // 3. Show success console.log("Success:", data); return data; } catch (err) { console.error("Request failed:", err); throw err; } } |
Quick Summary – Server Response Handling Cheat Sheet
| Question you must answer | How to check (XHR) | How to check (fetch) | Typical action |
|---|---|---|---|
| Did network work? | onerror or status === 0 | catch block | Show “No internet” / “Server unreachable” |
| Is status successful? | status >= 200 && status < 300 | response.ok | Proceed to parse body |
| What kind of data came back? | xhr.getResponseHeader(“Content-Type”) | response.headers.get(“content-type”) | Choose .json(), .text(), .blob()… |
| Is it JSON? | responseType = “json” or check header | response.json() | Parse automatically |
| Is it XML? | xhr.responseXML | response.text() + DOMParser | Use DOM methods to read |
Would you like to continue with one of these next?
- Different Content-Types (JSON, XML, HTML, image, file download…)
- How to automatically detect the right parsing method
- Upload progress and download progress
- Handling very large responses (streaming)
- CORS, timeout, abort, and retry patterns
- Converting this knowledge to modern fetch + async/await
Just tell me what you want to explore more deeply! 😊
