Lesson 23 of 30

Fetch API

Making HTTP Requests

The Fetch API provides a modern interface for making HTTP requests to servers, replacing the older XMLHttpRequest.

Fetch returns Promises, making it perfect to use with async/await for cleaner, more readable code.

You can fetch data from APIs, submit forms, upload files, and handle various HTTP methods (GET, POST, PUT, DELETE) and response types.

Example
// Basic GET request
fetch('https://api.example.com/users')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

// Using async/await (preferred)
async function fetchUsers() {
  try {
    const response = await fetch('https://api.example.com/users');

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error('Fetch error:', error);
  }
}

// POST request with JSON data
async function createUser(userData) {
  try {
    const response = await fetch('https://api.example.com/users', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(userData)
    });

    const result = await response.json();
    return result;
  } catch (error) {
    console.error('Error creating user:', error);
  }
}

// Different response types
const textData = await response.text();    // Plain text
const jsonData = await response.json();    // JSON
const blobData = await response.blob();    // Binary (images, files)
  • fetch(url) - Make HTTP request, returns Promise
  • response.json() - Parse JSON response body
  • response.text() - Get response as plain text
  • response.ok - Boolean, true if status is 200-299
  • response.status - HTTP status code (200, 404, etc.)
  • method - HTTP method: GET, POST, PUT, DELETE, etc.
  • headers - Set request headers (Content-Type, Authorization, etc.)
  • body - Request body for POST/PUT requests
Try Fetch API
HTML
<div class="fetch-demo">
  <h3>Fetch API Examples</h3>

  <div class="example">
    <h4>1. Fetch Users (GET Request)</h4>
    <button id="fetchUsersBtn">Fetch Users</button>
    <div id="usersOutput"></div>
  </div>

  <div class="example">
    <h4>2. Fetch Single User</h4>
    <input type="number" id="userId" value="1" min="1" max="10">
    <button id="fetchUserBtn">Fetch User</button>
    <div id="userOutput"></div>
  </div>

  <div class="example">
    <h4>3. Create Post (POST Request)</h4>
    <input type="text" id="postTitle" placeholder="Post title">
    <textarea id="postBody" placeholder="Post content"></textarea>
    <button id="createPostBtn">Create Post</button>
    <div id="postOutput"></div>
  </div>
</div>
CSS
.fetch-demo {
  padding: 20px;
  max-width: 700px;
}

.example {
  margin: 20px 0;
  padding: 15px;
  background: #f5f5f5;
  border-radius: 8px;
}

h4 {
  margin-top: 0;
  color: #333;
}

button {
  padding: 10px 15px;
  margin: 5px;
  background: #2196F3;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
}

button:hover {
  background: #1976D2;
}

input, textarea {
  display: block;
  width: 100%;
  padding: 8px;
  margin: 8px 0;
  border: 2px solid #ddd;
  border-radius: 4px;
  box-sizing: border-box;
}

textarea {
  min-height: 60px;
  font-family: inherit;
}

#usersOutput, #userOutput, #postOutput {
  margin-top: 10px;
  padding: 10px;
  background: white;
  border: 2px solid #ddd;
  border-radius: 4px;
  max-height: 200px;
  overflow-y: auto;
  font-family: monospace;
  font-size: 13px;
}

.user-card {
  padding: 8px;
  margin: 5px 0;
  background: #e3f2fd;
  border-left: 3px solid #2196F3;
  border-radius: 4px;
}

.success {
  border-color: #4CAF50 !important;
  background-color: #e8f5e9 !important;
}

.error {
  border-color: #f44336 !important;
  background-color: #ffebee !important;
  color: #c62828;
}
JavaScript
// Example 1: Fetch Multiple Users
document.getElementById('fetchUsersBtn').addEventListener('click', async function() {
  const output = document.getElementById('usersOutput');
  output.innerHTML = 'Loading users...';

  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/users');

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const users = await response.json();

    output.innerHTML = '';
    users.slice(0, 5).forEach(user => {
      output.innerHTML += `
        <div class="user-card">
          <strong>${user.name}</strong><br>
          Email: ${user.email}<br>
          City: ${user.address.city}
        </div>
      `;
    });

    output.innerHTML += `<br><em>Showing 5 of ${users.length} users</em>`;
  } catch (error) {
    output.className = 'error';
    output.textContent = 'Error: ' + error.message;
  }
});

// Example 2: Fetch Single User
document.getElementById('fetchUserBtn').addEventListener('click', async function() {
  const output = document.getElementById('userOutput');
  const userId = document.getElementById('userId').value;

  output.innerHTML = `Loading user #${userId}...`;

  try {
    const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`);

    if (!response.ok) {
      throw new Error(`User not found! Status: ${response.status}`);
    }

    const user = await response.json();

    output.innerHTML = `
      <div class="user-card">
        <strong>Name:</strong> ${user.name}<br>
        <strong>Username:</strong> @${user.username}<br>
        <strong>Email:</strong> ${user.email}<br>
        <strong>Phone:</strong> ${user.phone}<br>
        <strong>Website:</strong> ${user.website}<br>
        <strong>Company:</strong> ${user.company.name}
      </div>
    `;
    output.className = 'success';
  } catch (error) {
    output.className = 'error';
    output.textContent = 'Error: ' + error.message;
  }
});

// Example 3: Create Post (POST Request)
document.getElementById('createPostBtn').addEventListener('click', async function() {
  const output = document.getElementById('postOutput');
  const title = document.getElementById('postTitle').value;
  const body = document.getElementById('postBody').value;

  if (!title || !body) {
    output.className = 'error';
    output.textContent = 'Please fill in both title and content!';
    return;
  }

  output.innerHTML = 'Creating post...';

  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        title: title,
        body: body,
        userId: 1
      })
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const result = await response.json();

    output.innerHTML = `
      <strong>✓ Post Created Successfully!</strong><br><br>
      <strong>ID:</strong> ${result.id}<br>
      <strong>Title:</strong> ${result.title}<br>
      <strong>Body:</strong> ${result.body}<br>
      <strong>User ID:</strong> ${result.userId}
    `;
    output.className = 'success';

    // Clear form
    document.getElementById('postTitle').value = '';
    document.getElementById('postBody').value = '';
  } catch (error) {
    output.className = 'error';
    output.textContent = 'Error: ' + error.message;
  }
});
Notes
  • CORS: Cross-Origin Resource Sharing restrictions may prevent requests to some APIs from browsers.
  • Error Handling: Always check response.ok and handle network errors with try/catch.
  • API Keys: Never expose API keys in client-side code. Use environment variables or backend proxy for sensitive requests.