The Fetch API makes working with AJAX much simpler by using promises and a clean syntax. If you handle errors correctly and keep security in mind, you can build very reliable connections between your app and your server. Using these techniques will help you create fast, modern web applications that provide a great experience for your users.
Introduction
Web apps need to talk to servers in the background. Years ago, developers used XMLHttpRequest to handle these tasks, but it was often clunky and hard to read. The Fetch API is the modern standard for making network requests in the browser. It uses promises, which makes your code much cleaner. This guide shows you how to use Fetch for basic requests, handle errors properly, and follow best practices for real-world apps.
Understanding the Fetch API Basics
What Fetch Replaces
- The old, complex event model of XMLHttpRequest
- Deeply nested callbacks that make code hard to follow
- The need to parse different data formats manually
Core Syntax
Here is the basic pattern for a standard GET request:
```javascript
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
// Process the JSON payload
})
.catch(error => {
// Handle network or parsing errors
});
```
A few important things to remember:
- When you call `fetch`, it returns a promise that resolves to a `Response` object.
- You can use methods like `.json()`, `.text()`, or `.blob()` to get the data you need.
- The `catch` block only triggers for actual network failures. HTTP error codes, like 404, will not reject the promise automatically.
Handling HTTP Errors Explicitly
Since the promise doesn't fail just because the server returns an error code, you need to check the `ok` property yourself.
```javascript
fetch('https://api.example.com/resource')
.then(response => {
if (!response.ok) {
throw new Error(`Server responded with ${response.status}`);
}
return response.json();
})
.then(data => {
// Use the data
})
.catch(err => {
console.error('Request failed:', err);
});
```
Common Status Checks
- 400–499 – Client errors (bad request, unauthorized, not found)
- 500–599 – Server errors (internal error, service unavailable)
If you throw an error when these statuses appear, your `catch` block can handle every type of failure in one place.
Configuring Request Options
You can pass an options object as the second argument to `fetch` to customize your request.
#### Example: POST Request with JSON Body
```javascript
const payload = { name: 'Alice', role: 'Developer' };
fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify(payload),
credentials: 'include' // Send cookies for same origin requests
})
.then(response => {
if (!response.ok) {
throw new Error(`Failed with status ${response.status}`);
}
return response.json();
})
.then(result => {
console.log('User created:', result);
})
.catch(err => {
console.error('Error creating user:', err);
});
```
#### Important Options
- `method` – HTTP verb (GET, POST, PUT, DELETE, etc.)
- `headers` – Key‑value pairs for request headers
- `body` – Payload for methods that support a body (POST, PUT, PATCH)
- `mode`: This sets the CORS mode, such as `cors` or `same-origin`.
- `cache`: Use this to control how the browser handles caching.
- `credentials`: This determines how cookies are handled during the request.
Working with Query Parameters
Always encode your query strings to keep your URLs safe and valid.
```javascript
const params = new URLSearchParams({
search: 'fetch api',
page: 2,
limit: 20
});
fetch(`https://api.example.com/articles?${params.toString()}`)
.then(res => res.json())
.then(data => {
// Render articles
})
.catch(console.error);
```
The `URLSearchParams` interface automatically handles encoding of special characters.
Parallel and Sequential Requests
Parallel Requests with Promise.all
Running multiple requests at the same time is a great way to speed up your app.
```javascript
const urls = [
'https://api.example.com/users',
'https://api.example.com/posts',
'https://api.example.com/comments'
];
Promise.all(urls.map(url => fetch(url).then(r => {
if (!r.ok) throw new Error(`Failed ${url}`);
return r.json();
})))
.then(([users, posts, comments]) => {
// Process combined data
})
.catch(err => {
console.error('One of the requests failed:', err);
});
```
Sequential Requests with async/await
If you need one request to finish before starting the next, `async` and `await` make the code very readable.
```javascript
async function loadUserData(userId) {
try {
const userRes = await fetch(`https://api.example.com/users/${userId}`);
if (!userRes.ok) throw new Error('User not found');
const user = await userRes.json();
const postsRes = await fetch(`https://api.example.com/users/${userId}/posts`);
if (!postsRes.ok) throw new Error('Posts unavailable');
const posts = await postsRes.json();
return { user, posts };
} catch (e) {
console.error(e);
throw e;
}
}
```
Security Considerations
- Keep sensitive data out of URLs. Use the request body for things like login credentials.
- Clean your inputs. Make sure all data is safe before adding it to a request.
- Check your CORS settings. Ensure the server headers allow your origin to talk to the API.
- Always use HTTPS. This keeps data safe while it travels over the network.
- Prevent CSRF attacks. Be careful with requests that change state and use cookies.
Performance Optimizations
- Use caching. Set the right headers on your server and use the cache option in your fetch calls.
- Shrink your data. Turn on compression like gzip to make responses smaller.
- Limit your requests. Throttling on the client side prevents your server from getting overwhelmed.
- Stay on the same origin. The browser can reuse connections if your API calls go to the same place.
Testing Fetch Calls
- Mock your calls. Use tools like jest-fetch-mock so you don't have to hit a real server during tests.
- Check your params. Make sure your tests verify that you are sending the right headers and methods.
- Test for failure. Force your mocks to fail to make sure your error handling actually works.
Conclusion
The Fetch API makes working with AJAX much simpler by using promises and a clean syntax. If you handle errors correctly and keep security in mind, you can build very reliable connections between your app and your server. Using these techniques will help you create fast, modern web applications that provide a great experience for your users.