JavaScript Core/
Lesson

Every app you use pulls data from somewhere, a weather service, a database, a payment processor. In JavaScript, the tool that handles all of that is fetch. This lesson shows you how to use it and what to do when things go wrong.

What fetch() actually does

fetch() is a browser-native function that sends an HTTPWhat is http?The protocol browsers and servers use to exchange web pages, API data, and other resources, defining how requests and responses are formatted. request and gives you back a PromiseWhat is promise?An object that represents a value you don't have yet but will get in the future, letting your code keep running while it waits.. Think of it like ordering food at a restaurant: you place the order (the request), and the kitchen promises to get back to you. When the food arrives, you still need to unpack it (parse the response).

// The Promise-based way
fetch('https://api.example.com/users')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

That .then(response => response.json()) step is easy to forget, the first .then gives you the Response wrapper, not the data itself. You have to call .json() to unwrap it, and that call is also asynchronous.

02

Using async/awaitWhat is async/await?A syntax that lets you write asynchronous code (like fetching data) in a readable, step-by-step style instead of chaining callbacks.

async/await makes fetch calls much easier to read and reason about. Instead of chaining .then() callbacks, you write code that looks synchronous even though it isn't.

async function getUsers() {
  try {
    const response = await fetch('https://api.example.com/users');
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Failed to fetch users:', error);
  }
}

Notice the two await calls: one for the network request, one for parsing the body. Both are asynchronous operations. Forgetting the second await is one of the most common beginner mistakes, you'll end up with a PromiseWhat is promise?An object that represents a value you don't have yet but will get in the future, letting your code keep running while it waits. object instead of your data.

03

The Response object

When fetch() resolves, it gives you a Response object with useful properties you can inspect before doing anything with the data.

const response = await fetch('https://api.example.com/users');

// Check if request was successful (status 200-299)
console.log(response.ok);         // true or false
console.log(response.status);     // 200, 404, 500, etc.
console.log(response.statusText); // "OK", "Not Found", etc.
console.log(response.headers);    // Response headers

// Parse response body (pick one - body can only be read once)
const json = await response.json();  // Parse as JSON
const text = await response.text();  // Parse as plain text
const blob = await response.blob();  // Parse as binary (images, files)
Gotcha
The body can only be consumed once. If you call response.json() and then try response.text(), the second call will throw. Pick the method that matches what the server is returning.
PropertyTypeWhat it tells you
response.okbooleantrue if status is 200–299
response.statusnumberHTTP status code (200, 404, 500...)
response.statusTextstringHuman-readable status ("OK", "Not Found")
response.headersHeadersResponse headers object
response.bodyReadableStreamRaw body (consumed once)
AI pitfall
AI tools routinely generate fetch calls with zero error handling. They skip response.ok checks, omit try/catch, and assume the happy path always works. A fetch call without if (!response.ok) is incomplete, period. If Copilot hands you a bare fetch.json() chain, add the error handling yourself before moving on.
04

Building a reusable fetch wrapper

Writing raw fetch() calls everywhere gets repetitive fast. A small wrapper function cleans things up and centralizes your error handling in one place.

const params = new URLSearchParams({
  page: '1',
  limit: '10',
  search: 'javascript'
});

// URLSearchParams builds: ?page=1&limit=10&search=javascript
const response = await fetch(`https://api.example.com/posts?${params}`);
const data = await response.json();
async function fetchAPI(endpoint) {
  const response = await fetch(`https://api.example.com${endpoint}`);

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

  return response.json();
}

// Clean call sites
const users = await fetchAPI('/users');
const posts = await fetchAPI('/posts');

The if (!response.ok) check is critical here. Without it, a 404 or 500 response slips through silently and you'll be parsing an error message as if it were your data.

05

Quick reference

TaskCode
Basic GETawait fetch(url)
Parse JSON responseawait response.json()
Check successresponse.ok (true for 200–299)
Add query paramsnew URLSearchParams({ key: 'value' })
Handle network errortry/catch around the await fetch() call
Parse as textawait response.text()