An HTTP response combines status, headers, and an optional body. Decode the body according to its media type and the endpoint contract, not by assuming every response is JSON. A 204 response intentionally has no body, and an error representation may differ from a success representation.
When an XHR request completes, several properties on the object give you access to the response data:
// ---- Parsing JSON with responseType ----
const xhrJson = new XMLHttpRequest();
xhrJson.open('GET', '/api/users', true);
xhrJson.responseType = 'json'; // browser parses JSON automatically
xhrJson.onload = function () {
// xhr.response is already a JS object - no JSON.parse() needed
console.log(xhrJson.response[0].name);
};
xhrJson.send();
// ---- Parsing JSON manually from responseText ----
const xhrText = new XMLHttpRequest();
xhrText.open('GET', '/api/users', true);
xhrText.onload = function () {
try {
const users = JSON.parse(xhrText.responseText);
console.log(users);
} catch (e) {
console.error('Invalid JSON:', e.message);
}
};
xhrText.send();
// ---- Parsing XML ----
const xhrXml = new XMLHttpRequest();
xhrXml.open('GET', '/api/data.xml', true);
xhrXml.onload = function () {
const xmlDoc = xhrXml.responseXML;
const items = xmlDoc.getElementsByTagName('item');
Array.from(items).forEach(item => {
console.log(item.textContent);
});
};
xhrXml.send();
Always check the HTTP status code before processing the response. A completed XHR request (readyState 4) does not mean the request was successful.
| Code | Meaning | Action |
|---|---|---|
| 200 | OK | Process the response normally. |
| 201 | Created | Resource was created successfully. |
| 204 | No Content | Success but no body (common for DELETE). |
| 400 | Bad Request | Client sent invalid data. |
| 401 | Unauthorized | Authentication required. |
| 403 | Forbidden | Authenticated but not allowed. |
| 404 | Not Found | Resource does not exist. |
| 500 | Internal Server Error | Server-side bug. |
const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/resource/42', true);
xhr.responseType = 'json';
xhr.onload = function () {
switch (true) {
case xhr.status >= 200 && xhr.status < 300:
console.log('Success:', xhr.response);
break;
case xhr.status === 400:
console.error('Bad request - check your input data');
break;
case xhr.status === 401:
console.warn('Not authenticated - redirect to login');
window.location.href = '/login';
break;
case xhr.status === 403:
console.error('Access denied');
break;
case xhr.status === 404:
console.error('Resource not found');
break;
case xhr.status >= 500:
console.error('Server error - try again later');
break;
default:
console.warn('Unexpected status:', xhr.status);
}
};
xhr.onerror = () => console.error('Network failure');
xhr.send();
// ---- Download an image as a Blob ----
const xhrBlob = new XMLHttpRequest();
xhrBlob.open('GET', '/images/photo.jpg', true);
xhrBlob.responseType = 'blob';
xhrBlob.onload = function () {
const blob = xhrBlob.response;
const url = URL.createObjectURL(blob);
document.getElementById('preview').src = url;
};
xhrBlob.send();
// ---- Download binary data as ArrayBuffer ----
const xhrBuf = new XMLHttpRequest();
xhrBuf.open('GET', '/data/binary.bin', true);
xhrBuf.responseType = 'arraybuffer';
xhrBuf.onload = function () {
const buffer = xhrBuf.response;
const view = new Uint8Array(buffer);
console.log('First byte:', view[0]);
};
xhrBuf.send();
Read important headers such as Location, ETag, Retry-After, or pagination links when the API uses them. For large responses, streaming can reduce memory and show progress, but it requires incremental decoding and cancellation; response.json buffers and parses the complete body.
Separate transport DTOs from UI state. Validate required response fields at the boundary and normalize them once. This prevents every component from guessing about nulls, date strings, and error shapes and makes a changed API contract fail close to the request.
Explore 500+ free tutorials across 20+ languages and frameworks.