JS Sketchbooksee JavaScript think ✏️
← back to phase 9

9.7 — fetch without a browser

In 6.7 an envelope left a browser tab. Today the same envelope leaves a plain terminal — no page, no HTML, just a script having a data conversation with a server. JSON out, JSON in.

This tiny shift has a job title hiding inside it: API testing. By the end of the lesson you’ll have written your first API check — every tool for it is already on your belt.

watch it happen
// users.js — no HTML, no page, just Node
const res = await fetch("https://api.shop.com/users/7");

console.log(res.status);

const user = await res.json();
console.log(user.name);
console.log(user.pet?.name ?? "no pet");

// $ node users.js
// 200
// Ada
// no pet

The star is an old friend: fetch — 6.7’s envelope, round trip and all — is built into Node (since version 18). Line 2 works in a plain script with zero installs. The envelope simply leaves a terminal now instead of a browser tab.

your terminal$ node users.jsapi.shop.comthe server/users/7✉️6.7’s fetch exists in Node too (built in since Node 18) — the envelope nowleaves a TERMINAL
under the hood

The deeper story, with the real names for things — this part is what turns “I saw it” into “I can explain it.”

Sending data works the same way with options: fetch(url, { method: "POST", body: JSON.stringify(data) }) — 4.13’s stringify finally meets its purpose: objects travel as JSON text. GET reads, POST creates, PUT/PATCH update, DELETE removes — the HTTP verbs, worth recognizing on sight.

One honesty note carried over from 6.7: fetch only rejects on network failure. A 404 or 500 is a “successful” fetch with a bad status — which is precisely why API checks assert on res.status explicitly instead of trusting that “it didn’t throw.”

The browser’s CORS wall (6.7’s bouncer) doesn’t apply here — CORS protects users inside browsers, and there’s no browser. Scripts can call any API they’re authorized for; the authorization usually travels as a header carrying a token from… process.env (9.4’s secrets, completing the circle).

Job note: API tests make superb setup for browser tests too — create the user via the API in one second, then browser-test only the login screen itself. Phase 11 uses this trick constantly; it’s often the difference between a 40-second test and a 4-second one.

your turn

⌨️ your first API check

A fake server is provided (so the check is deterministic — no real network). Write the async function that performs a genuine API check: status first, then the parsed body, read safely.

requirements:

  • Keep the provided fakeFetch exactly as is — it stands in for the real network.
  • Write an async function checkUser(): await the response, print its status, await the parsed body, print its name.
  • Also print the pet’s name with an honest fallback of "no pet" — the response has no pet field, and your check must not crash (8.4’s pair).
  • Call checkUser() at the end.

when you press RUN, the console must show exactly:

200
Ada
no pet

✏️ Quick check 1

res.status is 404. Whose side of the conversation made the mistake — yours or the server’s?

✏️ Quick check 2

How many awaits does reading a JSON response take, and why? Type the number.

✏️ Quick check 3

The response body has no pet field. What does user.pet?.name ?? "no pet" evaluate to?

teach it back

🗣️ Now teach it back

Explain to a friend what API testing is, using today’s script: what fetch does without a browser, why status and body take separate awaits, and why ?. and ?? show up in almost every check.

Write it as if your friend is sitting next to you. Saved to your journal — future-you will use these notes to teach others.

a few sentences, minimum — you’ve got this
to remember
fetch is built into Node — the 6.7 envelope leaves a terminal: a pure data conversation, JSON out and in. GET/POST/PUT/DELETE are the verbs; POST bodies travel via JSON.stringify.
Two awaits, always: envelope (status/headers) → res.json() (parsed body). Assert res.status explicitly — fetch only rejects on network failure, never on 404/500.
This discipline = API TESTING: cheap, fast, CSS-immune. The pyramid (10.2) stacks many of these under fewer browser tests; Playwright wraps it as the request fixture (Phase 11).