Webbo3 Level 2 · JavaScript Development · Day 8 of 20
APIs and HTTP
How your JavaScript asks a computer in another country for data, and how to read what comes back.
By the end of today you can
- Explain what an API is and what happens between fetch() and the data arriving
- Read a status code and know whose problem it is
- Send GET and POST requests correctly, with the right headers and body
- Turn JSON into JavaScript objects and back again
- Recognise a CORS error on sight, and know why it is not your code
- Keep an API key out of your repository
An API is a door another company leaves open on purpose. Behind it is data you could never collect yourself: exchange rates, weather, football fixtures, a payment system, another team's database.
Today you learn to knock on that door properly, and to understand the answer that comes back. Yesterday gave you the tools for waiting; today is what you wait for.
Most browser examples call a small practice API at https://api.webbo3.test. It always answers the same way, so what you see in each box is exactly what the browser did with that answer. One example calls the real GitHub API, and in Do This Now you will call real public APIs yourself.
1. What an API Actually Is
Definition
An API (Application Programming Interface) is an agreed list of things one program may ask another program for, and the shape of the answer it will get back. A web API does this over HTTP: you send a request to a URL, and a response comes back, almost always as JSON.
Real-Life Example
You know the restaurant. You sit at a table, the waiter takes your order to a kitchen you never see, and brings food back. The API is the waiter and the menu: a fixed list of what you may order, and what arrives when you do.
You never see the kitchen, and you do not need to. The other team can rebuild their entire kitchen and your order still works, because the menu did not change.
Picture the waiter with a notepad. You can only order what is on the menu, you must order it the way the menu says, and what comes back is always served on the same kind of plate.
2. The Parts of a Request
Definition
Every HTTP request is made of the same few parts. The method says what you want done, the URL says which thing, headers carry information about the request, and the body carries data you are sending.
Real-Life Example
A letter to a company: the envelope says who it is for (the URL), a note on the front says "Urgent, reply in writing" (headers), the first word inside says what you want, "Please cancel" (the method), and the pages inside are the details (the body).
Types: The Parts
| Part | What it is | Example |
|---|---|---|
| Method | What you want done | GET, POST, PUT, PATCH, DELETE |
| URL | Which thing you want | https://api.webbo3.test/courses/2 |
| Headers | Information about the request | Content-Type: application/json |
| Body | Data you are sending | Only on POST, PUT and PATCH |
| Query string | Options on a GET | ?page=2&city=Lagos |
Types: The Methods
| Method | Means | Has a body |
|---|---|---|
| GET | Give me this. Never changes anything. | No |
| POST | Create a new one | Yes |
| PUT | Replace this one entirely | Yes |
| PATCH | Change these fields only | Yes |
| DELETE | Remove this one | Usually not |
A GET must never change anything on the server. Browsers, search engines and proxies all assume this and will re-send a GET whenever they like. Build a "delete" that works over GET and something will eventually crawl your links and empty your database.
Building a Query String
URLSearchParams builds the part after the ? for you, and handles spaces and special characters correctly.
Picture the letter again: the method is the first word, the URL is the address on the envelope, the headers are the stamps and notes on the front, and the body is what is folded inside.
3. Status Codes: Whose Problem Is It?
Definition
Every response carries a three-digit status code saying how the request went. The first digit tells you the family, and the family tells you whose problem it is.
Real-Life Example
Sending a parcel. "Delivered" is a 2xx. "Wrong address, returned to sender" is a 4xx: you made the mistake. "Depot on fire, delayed" is a 5xx: the courier has the problem, and there is nothing wrong with your parcel.
Types
| Code | Means | Whose problem |
|---|---|---|
| 200 | OK | Nobody, it worked |
| 201 | Created, after a POST | Nobody |
| 204 | Done, and there is nothing to send back | Nobody |
| 301, 302 | It moved | Nobody, follow it |
| 400 | Bad request: your JSON or fields are wrong | Yours |
| 401 | Unauthenticated: no valid key or token | Yours |
| 403 | Authenticated, but not allowed | Yours |
| 404 | Nothing at that URL | Yours |
| 429 | Too many requests, slow down | Yours |
| 500 | The server broke | Theirs |
| 503 | Server overloaded or down | Theirs |
4xx means you got it wrong. 5xx means they did. That one sentence tells you who to go and talk to, and it will save you hours.
Picture the parcel tracker: 2xx delivered, 4xx returned because of your label, 5xx stuck because of the courier.
4. JSON: The Format in the Middle
Definition
JSON (JavaScript Object Notation) is text that looks like a JavaScript object. APIs send and receive it because text travels over a network and objects do not. JSON.stringify() turns an object into JSON text; JSON.parse() turns JSON text back into an object.
Real-Life Example
Sending furniture across the country. You flat-pack the wardrobe into a box (stringify), ship the box, and the other side assembles it again from the instructions (parse). What travels is the flat box, never the wardrobe.
Types: The Two Directions
A readable version, for logging, uses two extra arguments: no filter, and indent by two spaces.
JSON has no functions, no undefined and no dates. A date becomes a string, a property whose value is undefined disappears, and a function is dropped.
That is why an API date arrives as a string like "2026-09-12T08:00:00.000Z", and you build a Date from it yourself with new Date(text).
Picture the flat-pack box. Only what fits in the box travels. Anything that cannot be written on the instructions, like a function, is left behind.
5. fetch: Asking for Data
Definition
fetch(url) sends an HTTP request and gives back a Promise (Day 7) of a response. The body of the response arrives separately, so you read it with response.json(), which is another Promise.
Real-Life Example
Ordering by phone. First the shop picks up and says whether they have it (the response and its status). Then they read out the details (the body). Two separate moments, so two separate waits.
Example 1: A GET Request
response.ok is true for any status from 200 to 299.
Common Mistake: fetch Does Not Fail on a 404
fetch only rejects when the request could not be made at all: no network, or blocked by the browser. A 404 or a 500 is a request that worked and brought back an error, so your catch never runs. Predict what this prints:
The catch never ran. The code read the error body as if it were a course, and got undefined. You must check response.ok yourself, every single time.
The Fix: Check response.ok and Throw
Example 2: No Network at All
A network failure arrives as a TypeError. That is how the complete pattern below tells "your connection" apart from "their server".
Overall Example: The Complete Pattern
This is the function you will copy into every project for the rest of the course. Every line is there because of something that goes wrong without it.
instanceof asks whether a value is a particular kind of object: here, whether the error is a TypeError.
Example 3: A Real API
The same fetch works outside the browser too. This one calls GitHub's real, public API and prints fields that do not change.
Picture phoning a shop: first they pick up and say whether they have it (status), then they read out the details (body). A shop that picks up and says "we do not stock that" still answered the phone, which is why fetch calls a 404 a success.
6. Sending Data
Definition
To create something, send a POST. You pass fetch a second argument: the method, a Content-Type header saying the body is JSON, and the body itself as a JSON string.
Real-Life Example
Filling in a registration form and posting it. The envelope says "registration" (the method and URL), a note says "form enclosed, in English" (the Content-Type), and the filled form is inside (the body).
The practice API replies 201, which means Created, with the new record and the id it was given.
Common Mistake: Sending the Object Itself
The body must be a string. Pass the object directly and JavaScript turns it into text the only way it knows:
"[object Object]" is not JSON, so the server replies 400. Leave out the Content-Type header and many servers will not read the body at all, which also ends in a 400.
Picture posting a registration: the form must be filled in on paper (a string), and the envelope must say what is inside, or the office cannot process it.
7. CORS: The Error That Is Not Your Fault
Definition
CORS (Cross-Origin Resource Sharing) is a browser rule. A page from one origin (its protocol, domain and port, such as http://localhost:5500) may only read a response from another origin if that server sends an Access-Control-Allow-Origin header saying it is allowed.
Real-Life Example
A security guard at an office will only hand you a letter addressed to you if the sender wrote "visitors may collect". The letter arrived, the guard has it, but without that note the guard will not give it to you.
What That Error Really Means
- The request usually went out, and the server usually answered. The browser blocked your page from reading the answer.
- It is a browser rule, not a JavaScript bug. The same request from a terminal or a server works fine, which is why it works in Node but not on your page.
- You cannot fix it from the frontend. No header you set and no fetch option changes it.
- The real fixes: use an API that allows browser requests, or put a small backend of your own in the middle, which is what you will do in Level 3.
They are advertised everywhere as the one-line fix. You would be routing your users' requests, and any key or token in them, through a stranger's server. Fine for a throwaway experiment on public data, never for anything real.
Picture the guard holding your letter: it arrived, but without the sender's permission note you cannot take it. Only the sender can add that note.
8. API Keys, and the Mistake That Costs Money
Definition
An API key is a secret string that identifies you to an API. Anything in your frontend JavaScript is visible to anyone who opens DevTools, so a key in the browser is effectively public, whatever the documentation calls it.
Real-Life Example
A key to your house. Taped to the front door, it is not a secret any more, however well you hid it among the other things on the door.
A key committed to a public GitHub repository is found by automated scanners within minutes. People have woken up to four-figure invoices from cloud providers because of one commit. If you ever commit one, rotate it (ask for a new key) immediately: deleting the commit does not help, because it is already in the history.
How to Keep a Key Out of Git
- Use APIs that need no key wherever you can.
- When a key is unavoidable, keep it in a file called
.env. - Add
.envto.gitignorebefore your first commit. - Commit a
.env.examplethat lists the names with no values, so others know what to set. - A key that must stay secret has to live on a server, not in the browser. That is Level 3.
Picture your house key again: it lives in your pocket (the server or .env), never taped to the front door (your JavaScript or your repository).
Do This Now
Use a real public API that needs no key. Two that work well: https://api.github.com/users/NAME (put in any GitHub username) and https://restcountries.com/v3.1/region/africa.
- Fetch the data and log it. Look at the real shape in the console before you write one line of rendering code.
- Add the response.ok check, and prove it by requesting a URL you know is a 404. Confirm your catch runs.
- Render the data onto the page with map and join, the Day 4 way.
- Open the Network tab in DevTools, refresh, and find your request. Read its status, its response headers and its JSON. This is where you will debug every API problem from now on.
- Go offline (DevTools, Network tab, choose Offline) and reload. Whatever your page does now, make it say something useful.
Checkpoint
Q1. A request returns 404. Does your catch block run?
No. fetch only rejects when the request could not be made at all. A 404 is a request that worked and brought back an error, so you check response.ok and throw yourself.
Q2. What is the difference between 401 and 403?
401 means the server does not know who you are: no key, or an invalid one. 403 means it knows exactly who you are, and you are still not allowed.
Q3. Why does body: student fail where body: JSON.stringify(student) works?
The body must be a string. An object turns into the text "[object Object]", which is not JSON, so the server rejects it with a 400.
Q4. Your fetch works in Node but is blocked in the browser. What is it?
CORS. The server is not sending an Access-Control-Allow-Origin header that allows your page's origin, so the browser will not let the page read the answer. It cannot be fixed from the frontend.
Tick Before You Move On
- ☐ I can explain what an API is using the restaurant
- ☐ I know 4xx is mine and 5xx is theirs
- ☐ I fetched real data and looked at its shape before rendering it
- ☐ My fetch checks response.ok, and I proved it with a deliberate 404
- ☐ I sent a POST with a JSON string body and a Content-Type header
- ☐ I found my request in the Network tab and read its status and headers
- ☐ I know what my page does with no connection, because I tried it
- ☐ .env is in my .gitignore
Quick Recap
An API is a menu of requests another program accepts, and the shape of its answers. A request has a method, a URL, headers and sometimes a body; GET never changes anything. 4xx is your mistake, 5xx is theirs. fetch does not throw on 404 or 500: check response.ok yourself, every time. JSON.stringify on the way out, response.json() on the way in; JSON drops dates, undefined and functions. CORS is a browser rule enforced by the server's headers, not a bug you can patch. Any key in frontend JavaScript is public; .env goes in .gitignore before the first commit.
Final Scenario
Your course dashboard needs the list of courses from the Webbo3 API. The student is on a train, the signal comes and goes, and one day the course they bookmarked is deleted.
Your apiGet function already handles all of it: the list when the network is good, a clear "check your connection" when it is not, and a readable message for the missing course, instead of a blank page.
An app that handles bad news clearly feels more trustworthy than one that only works on good wifi. Every request from now on goes through that one careful function.
Tomorrow, Day 9: working with real data. Real data is messy, slow and sometimes empty: searching, filtering and paging it without the interface lying to the user.