What you'll be able to do

By the end of this page you should be able to fetch a URL, read JSON with response.json(), use async/await, and know that HTTP error statuses (404, 500) do not reject the promise by themselves — you must check response.ok (next post goes deeper).

Previous post: async/await everyday.

Who this is for

  • People loading a public JSON API or a /api/... route from the browser
  • Beginners who only used static HTML until now
  • Anyone who saw fetch fail only on network errors and wondered why 404 looked “successful”

You can skip this if fetch + JSON is routine. Come back when status handling bites you.

Minimal GET

const res = await fetch("https://jsonplaceholder.typicode.com/todos/1");
const data = await res.json();
console.log(data);

Scenario A — public demo API. Fine for learning in the browser.

Scenario B — your own backend. Same shape: URL → response → parse body.

fetch returns a Promise for a Response object. The body is not parsed until you call .json(), .text(), etc. (also promises).

response.ok is not automatic throw

const res = await fetch("/api/missing");
console.log(res.status); // e.g. 404
console.log(res.ok); // false
// Promise still fulfilled — no throw yet

Scenario A — network down / CORS block. That rejects the promise — try/catch sees it.

Scenario B — server says 404. Promise fulfills with a Response; you must check ok or status. Full patterns in the next post.

Headers and method (light)

await fetch("/api/items", {
  method: "GET",
  headers: { Accept: "application/json" },
});

Scenario A — GET list. Default method is GET.

Scenario B — POST later. You will pass method: "POST" and often body: JSON.stringify(...) with Content-Type — keep that for when you build forms against a real API.

CORS in one sentence

Browsers block reading many cross-origin responses unless the server allows it. If the Network tab shows the request but your JS gets a TypeError, it may be CORS — not “fetch is broken.”

Scenario A — same origin (learn.aivoicepro.in calling its own /api). Usually fine.

Scenario B — random third-party API. Needs proper CORS headers or a backend proxy.

A tiny practice file

Save as fetch-basics.html and open via a local server if needed.

<!DOCTYPE html>
<html>
  <body>
    <button id="go">Load todo</button>
    <pre id="out"></pre>
    <script>
      document.getElementById("go").addEventListener("click", async function () {
        const out = document.getElementById("out");
        out.textContent = "loading…";
        try {
          const res = await fetch("https://jsonplaceholder.typicode.com/todos/1");
          const data = await res.json();
          out.textContent = JSON.stringify(data, null, 2);
        } catch (err) {
          out.textContent = "Network/CORS error: " + err.message;
        }
      });
    </script>
  </body>
</html>

Mistakes I see a lot

1. Assuming 404 throws. Check res.ok (next post).

2. Forgetting await res.json().

3. Double-parsing. Do not JSON.parse on an object you already got from .json().

4. Ignoring CORS. Read the console + Network tab.

5. Fetching on every keystroke without cancel. AbortController is Blog #59.

What to try before the next post

  1. Load a public JSON URL and print fields.
  2. Hit a bad URL path; log status and ok.
  3. Wrap fetch in try/catch and unplug network to see rejection.
  4. Build fetch-basics.html.

Next in this series: HTTP status and error handlingok, status checks, and linking to try/catch habits from Blog #22.

Try this next outside the series

Async code gets much easier when you see the same ideas on the browser side and the server side.