Quiz

What are the different ways to make an API call in JavaScript?

Topics
JavaScriptNetworking

TL;DR

Use the built-in fetch() API for most new browser and modern Node.js code. It supports promises, streaming response bodies, and cancellation with AbortController, but rejects only for request failures—not for HTTP error statuses—so check response.ok. Use XMLHttpRequest when maintaining legacy browser code or when its upload-progress events are specifically required. Libraries such as Axios can add a shared client policy, interceptors, and conveniences; jQuery AJAX mainly remains relevant in existing jQuery applications.

These are request clients. Server-Sent Events and WebSockets solve long-lived server push or two-way messaging and are not drop-in replacements for ordinary request-response calls.


Fetch API

A practical wrapper should handle status codes, cancellation, and the expected response format:

async function fetchUser(userId, { signal } = {}) {
const response = await fetch(`/api/users/${encodeURIComponent(userId)}`, {
headers: { Accept: 'application/json' },
signal,
});
if (!response.ok) {
throw new Error(`Could not load user (${response.status})`);
}
return response.json();
}
const controller = new AbortController();
try {
const user = await fetchUser('42', { signal: controller.signal });
console.log(user);
} catch (error) {
if (error.name !== 'AbortError') {
console.error(error);
}
}
// Call this when the screen is closed or a newer request supersedes this one.
controller.abort();

Cancellation stops work associated with this consumer where the platform can do so; it does not guarantee that the server rolls back work it already began. For writes, design server operations to be idempotent where retries are possible.

In browsers, cross-origin requests are governed by CORS and credentials default to same-origin behavior. Server-side fetch() is not protected by browser CORS, so server code must enforce its own outbound-request and response-size policies.

XMLHttpRequest

XMLHttpRequest is callback and event based. It is still useful in legacy code and for upload progress, which ordinary fetch() upload bodies do not expose consistently across browsers:

function uploadFile(file, onProgress) {
const xhr = new XMLHttpRequest();
xhr.open('POST', '/api/uploads');
xhr.responseType = 'json';
xhr.upload.addEventListener('progress', (event) => {
if (event.lengthComputable) onProgress(event.loaded / event.total);
});
xhr.addEventListener('load', () => {
if (xhr.status >= 200 && xhr.status < 300) {
console.log(xhr.response);
} else {
console.error(`Upload failed (${xhr.status})`);
}
});
xhr.addEventListener('error', () => console.error('Network error'));
const body = new FormData();
body.append('file', file);
xhr.send(body);
return () => xhr.abort();
}

Axios and other client libraries

Axios remains a useful option when a project benefits from one configured client for base URLs, interceptors, timeouts, serialization, or consistent behavior across its supported environments:

const api = axios.create({ baseURL: '/api', timeout: 10_000 });
const response = await api.get('/users/42');
console.log(response.data);

The tradeoff is another dependency and library-specific behavior. Check the versioned documentation rather than assuming it behaves exactly like fetch(). For example, Axios rejects non-success statuses by default, while fetch() normally fulfills with a Response for them.

jQuery AJAX

$.ajax() can be appropriate when the application already depends on jQuery. Adding jQuery solely to make requests is usually unnecessary in environments with fetch().

Choosing among them

Choose based on a requirement rather than age or syntax alone:

  • Start with fetch() for a new request-response client.
  • Keep XMLHttpRequest where upload progress or existing code depends on it.
  • Use a library when a shared, tested client policy justifies the dependency.
  • Use EventSource for one-way event streams and WebSocket for long-lived two-way messages.

Regardless of client, handle authentication, status codes, response validation, timeouts or cancellation, retries, and sensitive logging deliberately. Never embed server secrets in browser JavaScript.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

A modern browser application needs ordinary JSON requests, response streaming, and cancellation, but no upload-progress UI. Which built-in client is the best default?