api: Fetch, Explained by a Single HTML File

zubair-trabzada/api turns plain text, JSON, and POST requests into one small lesson in how the Fetch API actually feels.

8 min read • View on GitHub • More from zubair-trabzada

A wide black ink illustration of a workbench with one central switch routing documents into three distinct channels: a plain sheet, a stack of structured cards, and a sealed envelope. It shows how one fetch pipeline can read text, parse JSON, and send a POST body.
One interface, three kinds of data.
Key Takeaways

Most Fetch API tutorials stop at the happy path. This repo is more useful because it compares three different outcomes with almost no ceremony: reading raw text, loading local JSON, and posting data to a mock backend. That makes the hidden lesson obvious. Fetch is not one task. It is one interface with three very different shapes.

Why this repo earns its keep

The repository is tiny. The whole thing lives in a single index.html, plus users.json and sample.txt, with Bootstrap pulled from a CDN. That is why it works as a teaching artifact. There is nowhere for the data flow to hide.

It reads like a code-along project, not a product. That is a strength here, because the code shows the complete loop from click to fetch to render without abstractions getting between you and the browser.

Three buttons, three mental models

The UI maps one button to one async function. getText proves that fetch can return a plain string. getUsers shows that the same API can parse local JSON and feed it into map(). addPost closes the loop with a POST request, headers, and JSON.stringify().

fetch("sample.txt").then((r) => r.text());
fetch("users.json").then((r) => r.json());
fetch("https://jsonplaceholder.typicode.com/posts", {
  method: "POST",
  headers: { "Content-type": "application/json" },
  body: JSON.stringify({ title: "Post One", body: "Body", userId: 1 })
});

The same API handles reading, parsing, and writing, but each path ends differently.

The important part is the boundary between response headers and response bodies. The first then() gets a Response object. The next step chooses the parser. Text gets .text(), structured data gets .json(), and writes need a request body plus a content type.

  1. Click a button.
  2. Call fetch() against a file or URL.
  3. Choose text(), json(), or a POST body.
  4. Render the result into #output with a template literal.

What the repo leaves out on purpose

The code leans on innerHTML, which is fine for a sandbox and risky for production. It is fast to write, but it also means any untrusted content could become a problem if the source were malicious. The choice is honest. The repo optimizes for clarity, not hardening.

The rendering pattern is also worth noticing. Template literals act like a poor man's component system, just enough structure to turn raw data into HTML without a framework or build step. For a learning repo, that is the right amount of machinery.

How it compares

ApproachWhat it teachesWhat it costs
Fetch API sandboxThe native request pipeline from click to renderLight on structure, but easy to read and copy
XMLHttpRequest-era codeThe older callback-heavy way of doing the same workMore boilerplate and more mental overhead
Framework appState, components, and declarative renderingMore scalable, but farther from the browser's basics

That comparison is the point. If you are teaching yourself, or someone else, the native browser primitives, this repo is better than jumping straight to a framework. If you are shipping a larger app, you would keep the same ideas but wrap them in safer rendering, modular files, and stricter data boundaries.