- Learn
- Computer Science & IT
- Programming
- JavaScript Programming
- Fetching Data and Browser APIs
Fetching Data and Browser APIs
fetch, show the results on a web page and keep small amounts of data in the browser. You read HTTP requests, responses and JSON, handle loading and error states, and format dates and prices from an API for each reader. You find out why CORS errors happen and why secret API keys do not belong in front-end code, then use localStorage, sessionStorage and browser features such as geolocation and the clipboard.BeginnerLesson 8 of 9 in JavaScript Programming Course
What you will learn
- Explain how an HTTP request and response work, load JSON data with
fetch, and send data to an API with the right method, headers and body.- Read HTTP requests and responsesDescribe the parts of an HTTP request and response, match each HTTP method to the action it performs, and read status codes and headers in the browser's developer tools.
- Load JSON data with fetchCall
fetch, check the response status, read and parse the JSON body, and build request URLs with properly encoded query parameters. - Send data with POST, PUT, PATCH and DELETEPass an options object to
fetchto send JSON or form data, and handle the server's reply after creating, updating or deleting a record.
- Read HTTP requests and responses
- Turn API results into page content, format dates and money for the reader, and keep the page clear while data loads, fails or arrives out of order.
- Render API results into the pageBuild page elements from the arrays and objects in a JSON response, insert API text safely, and handle missing fields, empty results and paged results.
- Format dates, numbers and money from API dataConvert the date strings, timestamps and amounts that APIs send into readable text for the reader's language, region and time zone with the
Intlformatters. - Show loading, error and empty statesTrack the stages of a request, show the user a clear message for each stage, and recover from failures with retries.
- Cancel, time out and combine requestsAbort requests that are no longer needed, set time limits, stop outdated responses from replacing newer ones, and run several requests in parallel or in sequence.
- Render API results into the page
- Explain the same-origin policy and CORS errors, and protect API keys and user data when a page talks to other servers.
- Understand the same-origin policy and CORSIdentify the origin of a URL, explain why the browser blocks some cross-origin responses, and recognize how CORS headers and preflight requests let a server allow them.
- Protect API keys and user dataExplain what API keys and tokens do, why a secret in front-end code is public, and how to send credentials and handle user data responsibly.
- Understand the same-origin policy and CORS
- Save, read and update small amounts of data with localStorage and sessionStorage, and decide what is sensible and safe to keep in the browser.
- Save and read data with localStorage and sessionStorageUse the Web Storage methods, explain how long each kind of storage lasts and who can read it, and convert numbers, booleans and objects to and from stored strings.
- Use browser storage in an appRestore saved state when a page loads, cache API responses with an expiry time, keep tabs in sync, and decide what should not be stored.
- Save and read data with localStorage and sessionStorage
- Detect and use browser APIs that need a secure page or the user's permission, such as geolocation and the clipboard, and give users a fallback when a feature is unavailable.
- Get the user's location with the Geolocation APICheck that a browser API is available, explain secure contexts and permission prompts, and use the Geolocation API with its options, errors and a fallback.
- Copy text and use other browser APIsCopy text with the Clipboard API, react to connectivity changes, read parameters from the page address, and follow one method for learning any new browser API.
- Get the user's location with the Geolocation API
Fetching Data and Browser APIs study guide
1Make requests and read the replies
A page can ask a server for data without loading a new page. The request says which resource you want and what you want to do with it; the response tells you what happened. You will follow that exchange in the Network tab, turn a JSON reply into JavaScript data, and send changes back to the server. The important checkpoints are the method, the status, and the shape of the body: a response arriving does not by itself mean the operation succeeded.
1.1 The parts of an HTTP exchange
A web API lets a program read or change data through named URLs called endpoints. JavaScript on the page is the client. It sends an HTTP request; the server answers with an HTTP response. Many APIs send JSON text, which can represent objects, arrays, strings, numbers, booleans and null.
Read a request
Consider GET https://api.example.com/books?author=Le%20Guin&limit=5. The method is GET, the path /books selects the resource, and the query string contains two parameters. The author value is "Le Guin"; %20 encodes the space. An ampersand separates parameters. The API documentation explains which parameter names it accepts and what each value means.
A request can also carry headers, which are name–value pairs, and a body, which holds submitted data. Accept: application/json asks for a JSON response. Content-Type: application/json describes the body being sent. Authorization carries credentials. Header names ignore letter case, so content-type and Content-Type identify the same header.
Choose the action
| Method | Intended action | Typical use |
|---|---|---|
GET | Read | Get a list of notes |
POST | Create or submit | Add a note |
PUT | Replace | Send the complete replacement note |
PATCH | Change part | Mark one note done |
DELETE | Remove | Delete a note |
GET and HEAD are safe methods: their intended purpose is reading, so do not use GET for a delete button. GET, PUT and DELETE are idempotent. Repeating the same request leaves the server in the same intended state as performing it once. Deleting an already deleted note can return a different status, but the note remains absent. Repeating a POST can create a second record.
Interpret the reply
The response contains a status code, headers and usually a body. The first digit groups the outcome:
- 2xx: success.
200 OKcommonly has data,201 Createdreports creation, and204 No Contenthas no body. - 3xx: redirection. By default,
fetchfollows redirects. - 4xx: a problem with the request or access.
400means bad request;401means missing or invalid credentials;403means the server understands but refuses the request;404means not found;429means too many requests. - 5xx: a server problem, such as
500 Internal Server Erroror503 Service Unavailable.
A call to /books/42 might return 200, a JSON content type and {"id":42,"title":"The Dispossessed","year":1974}. A call to /books/9999 might return 404 and {"error":"Book not found"}. Both calls received a response. A connection failure produces no HTTP status for JavaScript to inspect.
Inspect before guessing
Open developer tools, select Network, and trigger the action. Inspect the method, full URL, status, headers, body and timing. Compare them with the documentation's endpoint, required parameters, credentials, rate limits and example response. If the server returned the right data, investigate the page code; if the request URL is wrong, fix that before changing the renderer.
Key Takeaways
- An endpoint is a URL that an API exposes for an operation.
- The method selects the action; the URL selects the resource and parameters.
- Request and response headers describe the exchange.
- A
404is a response, while a connection failure has no HTTP status. - Inspect the Network tab and compare the exchange with the API documentation.
1.2 Load JSON in two asynchronous steps
fetch(url) sends a GET request and returns a promise. Its result is a Response object, available when the headers arrive. Reading the body is a separate asynchronous operation. response.json() reads that body and parses it into JavaScript data.
Wait, check, read
async function loadBook(id) {
const response = await fetch(`https://api.example.com/books/${id}`);
if (!response.ok) {
throw new Error(`HTTP error ${response.status}`);
}
return await response.json();
}
For loadBook(42), the first await gets the response. The condition rejects an unsuccessful HTTP outcome. The second await gets the parsed book, such as { id: 42, title: "The Dispossessed", year: 1974 }. The function itself returns a promise, so its caller also waits for the result.
response.ok is true for statuses from 200 through 299. An HTTP error such as 404 or 500 does not reject the fetch promise: a server answered. Network failure, a blocked CORS exchange, an invalid request URL or cancellation can reject it. Throwing on !response.ok lets a surrounding try/catch handle HTTP failures too.
Understand the body
Without await, const data = response.json() stores a promise, not the book. Accessing data.title then gives undefined. Leaving await off fetch similarly leaves a promise where the code expects a Response.
Choose the body reader for the response: json() for JSON, text() for text, or blob() for binary data. A body can be consumed only once. Store the parsed result and reuse it. To inspect raw JSON text as well, read text() once, then call JSON.parse(text) on that stored string.
Invalid JSON rejects with a SyntaxError. An HTML error page often causes an "Unexpected token '<'" message. An empty body, including a 204 reply, cannot be parsed as JSON either. Check the status first and inspect response.headers.get("Content-Type") when the format is in doubt. A success status alone does not promise a JSON body.
Build a query safely
const url = new URL("https://api.example.com/search");
url.searchParams.set("q", "rock & roll");
url.searchParams.set("limit", 10);
The resulting address is https://api.example.com/search?q=rock+%26+roll&limit=10. The embedded ampersand is encoded as part of the value. Gluing input onto "?q=" can give &, # or + their URL meanings and change the search. A relative URL follows normal address resolution: from https://notes.example.org/app/index.html, fetch("data/books.json") loads /app/data/books.json on that origin.
Follow the returned shape
For {"results":[{"title":"Dune"}],"total":1}, the first title is data.results[0].title. The outer object is not the results array. Use the response preview to find the array before looping.
A shared getJSON(url, options) helper can perform the same status check and body read for many calls. In a .then() chain, return response.json() from the response callback so the next callback receives parsed data. Attach .catch() at the end to handle rejected steps.
Key Takeaways
fetchandresponse.json()return separate promises.- Check
response.ok; an HTTP error does not makefetchreject. - Read a response body once and reuse the stored value.
- Use
URL.searchParamsto encode query values. - Read nested JSON according to its actual object and array structure.
- A successful response can still contain an empty or invalid JSON body.
1.3 Send a change and use the server result
The second argument to fetch configures a request. Its options include method, headers, body, signal and credentials. Without these options, the request is a GET with no body. To submit JSON, you must prepare both its text and its content type.
Send JSON
const response = await fetch("https://api.example.com/notes", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ title: "Shopping", text: "Rice, lentils" }),
});
JSON.stringify produces {"title":"Shopping","text":"Rice, lentils"}. The header tells the server how to read that text. Passing the object directly as the body sends "[object Object]". Sending the JSON string without the header labels it text/plain;charset=UTF-8, which many JSON endpoints reject with 400 or 415 Unsupported Media Type.
A successful create often returns 201 with the stored record, such as {"id":17,"title":"Shopping","text":"Rice, lentils"}. Use this returned object to update the page: the server's new id is needed for later edits and deletion.
Replace or update
| Request | Body | Meaning |
|---|---|---|
PATCH /notes/17 | {"done":true} | Change the supplied field |
PUT /notes/17 | The complete replacement note | Replace the resource |
DELETE /notes/17 | Often none | Remove the resource |
A PUT body that omits a field can cause that field to be removed or reset, according to the API. Use the documented method for the intended change. PUT and PATCH may return an updated record with 200, or return 204 without a body.
Handle success without assuming that JSON always follows:
if (!response.ok) {
throw new Error(`HTTP error ${response.status}`);
}
const saved = response.status === 204
? null
: await response.json();
This pattern fits endpoints documented to return either JSON or 204. A delete can succeed and still produce a parse error if code blindly calls json() on its empty body.
Choose the body format
new FormData(formElement) collects named form fields and selected files. Pass it directly as body. Let the browser set Content-Type: multipart/form-data and its boundary; a manually supplied header can omit the boundary needed to separate the fields. A URLSearchParams body uses application/x-www-form-urlencoded.
GET and HEAD cannot carry a body through fetch. Put search terms and filters in the URL's query string. Attempting a GET with a body rejects with a TypeError before it can serve as a search request.
Finish safely after success or failure
Disable the submit button before awaiting a create request, then re-enable it in finally. A double click can otherwise send two POST requests and create two records. Do not automatically repeat an uncertain create operation: a lost reply does not establish whether the server made the change.
For an error status, the body may contain a useful message such as {"error":"title is required"}. Read it once if it is JSON; catch a failed parse and keep a general HTTP message for empty or HTML replies. Keep the user's entered text available so they can correct the request.
Key Takeaways
- JSON requests need
JSON.stringifyand a matching content type. - Use
PATCHfor a partial change andPUTfor a complete replacement. - Skip JSON parsing for
204 No Content. - Let the browser set the multipart header when sending
FormData. - Use the server reply, including its assigned ID, to update the page.
- Disable duplicate submissions and restore controls in
finally.
2Turn responses into a useful page
Fetching data is only the first part of an interface. Readers need useful results, readable dates and prices, and a clear message while they wait. You will build the display from the response shape, keep missing values from breaking it, and make success, empty results and failures visible. You will also control overlapping requests so a slow reply cannot replace newer results, and choose when independent requests can run together.
2.1 Build the display from data
Rendering turns an array or object into elements on the page. Keep the fetched data in a variable and give it to a rendering function. That separation lets you sort or filter the same data without asking the server for it again.
Create one element per item
function renderBooks(books, list) {
list.replaceChildren();
for (const book of books) {
const item = document.createElement("li");
item.textContent = `${book.title} (${book.year})`;
list.append(item);
}
}
For [{"title":"Kindred","year":1979},{"title":"Beloved","year":1987}], this creates two list items: "Kindred (1979)" and "Beloved (1987)". Clearing the container first makes a new search replace the previous search. Without that step, the old and new lists accumulate.
Keep external text as text
Use textContent for API titles, descriptions and other text. innerHTML parses a string as markup. A title containing <img src="x" onerror="alert(1)"> can then execute code. This is cross-site scripting, or XSS. Text from forms and storage needs the same treatment because it may also have come from an untrusted source.
Element properties set individual values, such as img.alt = book.title. They do not make every kind of value safe automatically. An API-supplied link still needs a protocol check:
try {
const target = new URL(book.url, location.href);
if (target.protocol === "https:" || target.protocol === "http:") {
link.href = target.href;
}
} catch {
link.removeAttribute("href");
}
This code runs on a newly created link without an existing href. An invalid or disallowed URL leaves it without a destination. A javascript: URL is not ordinary web navigation. For images, provide meaningful alt text built from the data when possible.
Supply defaults without losing real values
Optional fields may be absent or null. book.author?.name ?? "Unknown author" first tries the nested name and then supplies a default. Optional chaining avoids accessing a property of a missing author.
The operator matters: 0 || "n/a" gives "n/a", while 0 ?? "n/a" keeps 0. The || operator replaces all falsy values, including zero and an empty string; ?? replaces only null and undefined. A book with zero available copies needs to display zero.
An empty results array is a successful search with no matches. Show "No books match your search." so readers can distinguish it from a page that has not loaded.
Replace a search, append another page
Many APIs return pages using page, limit, a total count or a next URL. With 47 results and 10 per page, four full pages hold 40 records and the fifth holds 7. A new search clears the previous list; "Load more" appends the next page to the current list. Keep those two display actions separate.
To sort a local copy newest first, use [...books].sort((a, b) => b.year - a.year) and render that copy. The spread creates a new array, so the saved response order remains available. Inspect the response shape before selecting the array: a paged API often puts it inside data.results.
Key Takeaways
- Render from saved data so local sorting needs no new request.
- Insert untrusted text with
textContent. - Validate an API link’s protocol before assigning its destination.
- Use
?.for missing nested data and??when zero must stay zero. - Replace results for a new search; append results for another page.
2.2 Format dates and amounts for people
APIs return values that are easy for programs to exchange. Readers need dates, times and amounts formatted for their language and region. Keep the stored value separate from the text used to display it.
Read the date format first
"2026-03-05T14:30:00Z" names one instant: 14:30 UTC on March 5, 2026. The Z means UTC. new Date(...) turns it into a JavaScript Date. A date-only string such as "2026-03-05" is interpreted as midnight UTC; a date and time without an offset, such as "2026-03-05T14:30", is interpreted as local time.
A Unix timestamp counts from January 1, 1970, UTC. APIs often count seconds, but Date takes milliseconds. new Date(1772721000 * 1000) gives the same March 5 instant. Omitting the multiplication gives a date in January 1970. Read the API documentation to identify the unit.
Pick a locale and time zone
const date = new Date("2026-03-05T14:30:00Z");
const dateFormat = new Intl.DateTimeFormat("en-US", {
dateStyle: "medium",
timeStyle: "short",
timeZone: "UTC",
});
dateFormat.format(date);
The output is "Mar 5, 2026, 2:30 PM". With en-GB, it is "5 Mar 2026, 14:30". Using short dates produces "3/5/26" in en-US, "05/03/2026" in en-GB and "05.03.26" in de-DE.
The locale controls presentation; the time zone controls the local clock time. Without a timeZone, the formatter uses the device's zone. That instant is 9:30 AM in New York, 15:30 in Amsterdam and 11:30 PM in Tokyo. Keep and send instants in UTC and convert for display. A calendar date such as a birthday needs care: formatting midnight UTC in a zone west of UTC can show the previous day.
Keep currency separate from locale
const priceFormat = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "EUR",
});
priceFormat.format(1234.5);
This gives €1,234.50. For the same amount, de-DE with EUR gives 1.234,50 €; en-US with USD gives $1,234.50; en-US with JPY gives ¥1,235. JPY has no minor currency unit, so its normal display has no decimal places. Selecting a locale does not convert between currencies. Use the currency code supplied with the amount.
Some payment APIs send integer minor units. With USD, 1999 cents means 19.99 dollars: divide by 100 before formatting. Do not apply that divisor to JPY. Adding integer cents gives 1999 + 2550 = 4549, which formats as $45.49. This avoids the familiar decimal arithmetic result 0.1 + 0.2, which is 0.30000000000000004.
Reuse the formatter
Create a formatter once for a list. Use undefined as its locale to select the browser default; navigator.language reports the browser's preferred language tag. Other options format 0.256 as "26%", or 12.5 with unit "kilometer-per-hour" as "12.5 km/h". new Intl.RelativeTimeFormat("en", { numeric: "auto" }).format(-1, "day") gives "yesterday". Hand-built dates such as "03/05/2026" remain ambiguous across regions.
Key Takeaways
- Check whether timestamps use seconds or milliseconds.
- An explicit time zone makes date-time display predictable.
- Locale controls formatting; currency comes from the data.
- Convert documented minor units before formatting money.
- Reuse
Intlformatter objects across a list. - Date-only strings parse as UTC and can shift day when displayed in another zone.
2.3 Show what the request is doing
A request has a visible life cycle: idle, loading, then success or error. Success may contain data or an empty result. The page should explain each state so readers know whether to wait, change a search or try again.
Keep setup and cleanup together
In this handler, getJSON checks the HTTP status and parses the body, searchUrl builds the request URL, and renderResults updates the list. The button and status element already exist on the page.
async function search(query) {
button.disabled = true;
status.textContent = "Loading…";
try {
const data = await getJSON(searchUrl(query));
renderResults(data.results);
status.textContent = data.results.length === 0 ? "No results." : "";
} catch (error) {
status.textContent = "Could not load results. Check your connection and try again.";
console.error(error);
} finally {
button.disabled = false;
}
}
Setup happens before waiting. The success path renders results and handles an empty array. The catch replaces the loading message if any awaited step fails. finally restores the button after either path. If cleanup appeared only after a successful await, a failure would leave the control disabled.
Explain the useful cause
An HTTP error has a status to guide the message. For 404, say the item was not found. For 401, ask the user to sign in; for 403, explain that access is not allowed. For 429, ask them to wait. For 5xx, explain that the service has a problem and suggest trying later.
A TypeError from fetch can mean a network failure, a blocked CORS request or a bad URL. JavaScript does not get a reliable detailed CORS diagnosis; inspect the browser console and Network tab. A SyntaxError from json() points to invalid JSON. Cancellation and time limits produce errors such as AbortError and TimeoutError. Keep technical details in the console and give readers an action they can understand.
Make changes noticeable
Use an existing status element such as <p role="status"></p>. Its changes are announced to screen readers without moving focus. aria-live="polite" serves a similar purpose; role="alert" suits urgent errors. A spinner alone does not communicate an empty result or a failed request.
A Retry button calls the loading function again. Retrying unchanged 400 or 404 requests usually repeats the problem. Temporary network or service failures can justify a small, bounded retry sequence. Exponential backoff increases the pause, for example 1 second, 2 seconds, then 4 seconds. Respect a Retry-After response header; it may give a delay in seconds or an HTTP date. Avoid automatically retrying a create request that might already have succeeded.
If a refresh fails but earlier data is still useful, keep it visible with a note such as "Showing results from 10:42. Could not refresh." Do not silently make old results look current.
Test the difficult paths
Use developer tools to slow the connection, go offline, and request a missing path. Check that loading appears promptly, a failed request replaces it, an empty success has its own message, and controls become usable again.
Key Takeaways
- Make loading, empty success, data success and errors visible.
- Put shared cleanup in
finally. - Give readers a useful action and keep technical details in the console.
- Announce status updates through an appropriate live region.
- Bound retries and respect the server’s requested delay.
2.4 Control overlapping and parallel requests
Requests can outlive the action that started them. A search reply may arrive after the reader has typed something else, and a slow service may leave a page waiting too long. Control which results are still relevant as well as whether each request succeeded.
Cancel work you no longer need
Create an AbortController, pass its signal to fetch, then call abort() to cancel the request. Cancellation can interrupt either the wait for headers or an ongoing body read. The rejection is normally named "AbortError".
const controller = new AbortController();
const response = await fetch(url, { signal: controller.signal });
// A cancel button can call controller.abort().
const data = await response.json();
Wrap both awaits in error handling. If your code cancels an outdated search on purpose, return quietly for that expected abort instead of telling the user that the service failed.
fetch has no built-in timeout option. Use a signal such as AbortSignal.timeout(5000) to limit the request to 5000 ms, or 5 seconds of active time. Before headers arrive it rejects with TimeoutError; after headers arrive, it can interrupt body reading.
Stop old searches from winning
A race condition occurs when completion order changes the result. A reader types "ca" and then "cat". If "ca" returns last and is rendered, the screen no longer matches the input.
One approach aborts the previous request before each new request. Another assigns each call a number and only lets the newest call update the page:
let latest = 0;
async function search(query) {
const request = ++latest;
try {
const data = await getJSON(searchUrl(query));
if (request !== latest) return;
renderResults(data.results);
} catch (error) {
if (request !== latest) return;
showError(error);
}
}
The guard also prevents an old failure from replacing a newer success. Apply the same relevance check to loading messages and control cleanup when calls overlap.
Debouncing reduces how many calls begin. Clear the previous timer on each keystroke and start a new one, for example for 300 ms. Typing "paris" quickly then starts one request after the pause. Debouncing saves requests; it does not by itself prevent an earlier in-flight response from arriving late.
Decide what can run together
Independent requests can start together:
const [user, posts] = await Promise.all([
getJSON(userUrl),
getJSON(postsUrl),
]);
For durations of 300 ms and 500 ms, the pair takes about 500 ms instead of about 800 ms in sequence. Promise.all rejects as soon as one input rejects, but it does not cancel the other operations.
Use Promise.allSettled when partial results are useful. It waits for every input and reports each as { status: "fulfilled", value } or { status: "rejected", reason }. A dashboard can then show successful panels beside a failed one.
Dependent requests must stay in sequence. If a second URL needs an ID returned by the first request, wait for that ID before constructing the second request.
Key Takeaways
- Pass an abort signal before calling
abort(). - Catch errors from both fetching and reading the body.
- Ignore outdated results and outdated errors.
- Debouncing reduces calls; a relevance check prevents stale updates.
- Use
Promise.allfor independent work that must succeed together. - Use
Promise.allSettledwhen partial results are useful. - Wait for prerequisite data before starting a dependent request.
3Read cross-origin data and protect credentials
A request can look correct and still fail in a browser because the page and API belong to different origins. That boundary is controlled jointly by browser rules and the server’s response headers. You will identify the origin, interpret preflight and CORS failures, and distinguish permission to read a response from permission to perform an action. Credentials need a separate decision: anything delivered to a browser can be inspected by its user.
3.1 Understand the browser’s origin boundary
An origin combines a URL's scheme, host and port. A path is not part of it. https://shop.example.com/cart and /help on the same site share an origin. Changing to http://shop.example.com, https://api.example.com or https://shop.example.com:8443 changes the origin. HTTPS normally uses port 443.
Who allows the response to be read?
The same-origin policy limits which resources a page script can read. For a cross-origin fetch, CORS lets the API server grant access through response headers. A page at https://notes.example.org calling https://api.example.com/weather needs the API's permission to read the response.
Access-Control-Allow-Origin: https://notes.example.org grants that origin access. The value * permits any origin for a request without credentials. If the response has no matching permission, the browser hides it from the page and fetch rejects with a TypeError. The console gives the CORS diagnosis; JavaScript does not receive the detailed reason.
Adding Access-Control-Allow-Origin to the JavaScript request does not grant permission. It is a response header controlled by the server. Adding a custom request header can create an extra preflight check instead.
Why OPTIONS appears before POST
Some cross-origin requests need a preflight. The browser sends OPTIONS first, listing the intended method in Access-Control-Request-Method and non-safelisted header names in Access-Control-Request-Headers. The server permits them with Access-Control-Allow-Methods and Access-Control-Allow-Headers, along with the origin permission.
A basic GET, HEAD or POST with only allowed headers can avoid preflight. The permitted content types include text/plain, application/x-www-form-urlencoded and multipart/form-data. A JSON POST, an Authorization header, PUT or DELETE requires preflight unless a valid earlier permission is cached. If preflight is refused, the real request is not sent.
For a simple request, the real request can reach the server even when the browser later blocks reading its response. CORS therefore does not replace server authorization or input checking.
Cookies add another requirement
The default credentials: "same-origin" sends cookies only for same-origin requests. credentials: "include" allows eligible cookies on cross-origin requests too. Cookie SameSite rules and third-party-cookie policies still apply.
To let JavaScript read a credentialed response, the server must send Access-Control-Allow-Credentials: true and name the page's origin explicitly. Access-Control-Allow-Origin: * does not work with credentials. These headers do not force a browser to send an otherwise blocked cookie.
Diagnose without hiding the problem
mode: "no-cors" gives an opaque response with status 0 and no readable body or headers. It cannot provide JSON to a page. A request working in curl also does not establish browser CORS permission: terminal clients do not enforce that browser policy.
Inspect the console and Network tab. Check whether the preflight failed or the final response lacked permission. Fix the API's response configuration or route the call through your own server. During development, serve pages from a local server: file:// pages commonly have opaque origins. A local server still needs CORS permission when it calls a different origin.
Key Takeaways
- An origin consists of scheme, host and port.
- The server’s response headers grant cross-origin reading permission.
- Preflight checks a proposed method and headers before the real request.
- CORS can hide a response after a simple request has already been processed.
- Credentialed responses require an explicit origin and a credentials header.
no-corsproduces an opaque response, not readable JSON.
3.2 Keep secret keys off the page
An API key identifies a calling application for usage, limits and billing. An access token grants access for a user or session. Anyone holding a usable credential may be able to act under its authority, so decide where it belongs before adding it to a request.
Headers protect placement, not secrecy from the user
Providers may require Authorization: Bearer <token>, X-API-Key: <key> or a query parameter such as api_key. Follow the API documentation. A header avoids putting the credential into a full URL that can enter logs and analytics. An Authorization or custom header on a cross-origin call normally triggers preflight.
A header is still visible in developer tools. Visitors can inspect the JavaScript they download and the requests it sends. Minifying code or hiding a value behind a variable does not make it secret.
Follow the key through a weather request
Suppose browser code calls https://api.weather.example/v1/current?city=Lima&key=sk_live_4f9a2c. Anyone who opens Network can copy the key and use the account's quota. Moving that value into a build-time environment variable does not help if the build inserts it into the JavaScript delivered to the page.
A server-held secret changes the path:
- The page calls its own site at
/api/weather?city=Lima. - That site's server adds the secret and contacts the weather provider.
- The server returns the weather data to the page, keeping the secret out of the response.
The server still needs to control access and usage of its endpoint. Keeping the upstream secret on a server does not make unlimited public use harmless.
Some providers issue browser keys meant to be public. Apply the provider's domain restrictions, limit permitted APIs, and set usage quotas. Use a key this way only when its documented design allows it; a secret server key does not become a browser key by adding a domain restriction.
Respond to exposure
Keep credential files out of version control, for example by listing .env in .gitignore. This protects the file from being committed; it does not prevent a front-end build from publishing a value that the code embeds. If a key is exposed, revoke it and issue a replacement. Removing it from the newest commit leaves earlier history and any copies intact.
Protect the exchange and reduce waste
Use HTTPS for API calls so data and credentials are encrypted in transit. An HTTPS page's fetch to an HTTP API is blocked as mixed content. Encryption does not stop the person using the browser from seeing their own request.
A rate limit caps usage, for example at 60 requests per minute. Calls above the limit can return 429 Too Many Requests. Cache appropriate results, debounce search input and avoid duplicate calls. If the server supplies Retry-After, honor its delay instead of repeatedly retrying immediately.
Client-side validation makes forms easier to use, but anyone can call an endpoint without your form. The server must validate requests and authorize actions itself. When displaying its response, continue to treat strings as untrusted text and insert them with textContent.
Key Takeaways
- Browser-delivered code and requests expose any credentials they contain.
- A secret key belongs on the server that calls the provider.
- Restrict public browser keys by domain, API and quota.
- Revoke an exposed key; deleting it from current code is insufficient.
- HTTPS encrypts transit, while the server still validates and authorizes requests.
4Keep small amounts of state in the browser
A setting or draft can survive a reload without a new server request. Browser storage makes this convenient, but its values are strings and its lifetime depends on the store you choose. You will save and restore small pieces of state, validate old data before using it, and give cached API results an expiry time. You will also decide which data belongs elsewhere, since local storage is neither a secret vault nor the only safe copy of important work.
4.1 Save strings and restore their types
Web Storage offers two key–value stores: localStorage and sessionStorage. They use the same methods, so the main choice is how long the data should last and which tabs should share it.
Choose the lifetime
| Store | Lifetime | Sharing |
|---|---|---|
localStorage | Survives tab and browser closure until removed or cleared | Shared by pages of the same origin |
sessionStorage | Survives reloads during the tab's page session | Separate for each tab and origin |
Closing the tab ends its session storage. A newly opened page with an opener can start with a copy of the opener's session storage, but later changes remain separate. Avoid using session storage as a way to keep information secret from scripts.
Storage follows origins. https://app.example.com cannot share its store with http://app.example.com. Paths on the same origin do share it, so a prefix such as "notes-app:settings" helps keep multiple apps' keys distinct.
Use the storage methods
localStorage.setItem("notes-app:theme", "dark");
const theme = localStorage.getItem("notes-app:theme");
localStorage.removeItem("notes-app:theme");
setItem creates or overwrites an entry. getItem returns its string value or null if it is absent. removeItem removes one entry. clear() removes all entries from that storage area for the origin, which can affect other features on the same site. length and key(index) let you inspect the keys.
Use these methods instead of property assignments such as localStorage.theme = "dark". Keys such as "length" or "key" can collide with built-in members when accessed as properties.
Convert values deliberately
After localStorage.setItem("count", 5), reading gives "5". Adding 1 directly creates "51". Number(localStorage.getItem("count")) + 1 produces 6.
A stored boolean has a similar trap. setItem("darkMode", false) stores "false", and a nonempty string is truthy. Read with localStorage.getItem("darkMode") === "true" when those are the two expected stored strings.
Objects and arrays need JSON:
localStorage.setItem("settings", JSON.stringify({
theme: "dark",
fontSize: 18,
}));
const settings = JSON.parse(localStorage.getItem("settings"))
?? { theme: "light", fontSize: 16 };
Without serialization, an ordinary object becomes "[object Object]". JSON.parse(null) gives null, so the default covers a missing entry. A malformed stored string still needs try/catch, and parsed data needs a shape check before use.
Keep the workload small
Web Storage calls are synchronous: JavaScript waits for each one on the main thread. Use it for settings, a draft or a short list, not large collections or files. IndexedDB provides asynchronous storage for larger structured data.
Current browsers commonly allow about 5 MiB in each Web Storage area per origin. A full store can make setItem throw QuotaExceededError; access may also fail under browser privacy settings. Handle failure without preventing the rest of the app from working. Developer tools expose the stores in Chrome and Edge's Application tab or Firefox's Storage tab, where deleting entries helps test first visits.
Key Takeaways
- Choose
localStoragefor persistent shared settings andsessionStoragefor tab-session state. - Storage areas are separated by origin.
getItemreturns a string ornull.- Serialize objects with JSON and restore their types on reading.
- Handle storage failures and keep synchronous writes small.
4.2 Restore state and expire cached data
An app usually loads on start and saves on change. Read a saved list when the page opens, render it, and save the updated list after an edit. Browser storage is a convenience copy: it can be missing, damaged, outdated or cleared.
Validate before restoring
This loader expects a saved array of strings:
function loadTodos() {
try {
const value = JSON.parse(localStorage.getItem("todos"));
if (Array.isArray(value) && value.every(item => typeof item === "string")) {
return value;
}
} catch {
// Continue with an empty list if reading or parsing fails.
}
return [];
}
JSON.parse alone does not prove the value is an array or that its items fit the app. Valid JSON such as 42 still has the wrong shape. A versioned value such as { version: 2, todos: [...] } lets newer code recognize older formats and either convert them or restore a default.
Save the in-memory list with localStorage.setItem("todos", JSON.stringify(todos)) after each change. Catch write failures too; a page should be able to keep working even when it cannot preserve the next reload's state. A form draft that belongs only to one visit can use sessionStorage.
Give cached responses a timestamp
localStorage.setItem("weather", JSON.stringify({
savedAt: Date.now(),
data,
}));
On a later load, first validate the cached object's shape and timestamp. Its age is Date.now() - savedAt. Use it only while that age is less than 10 * 60 * 1000, or 600,000 ms. A value saved at 10:00:00 is 450,000 ms old at 10:07:30 and can be used. At 10:12:00, it is 720,000 ms old, so fetch a fresh value. At exactly 600,000 ms it is expired under this rule.
Expiry is app logic: localStorage does not delete an entry when your freshness interval ends. Reject a missing or invalid timestamp instead of treating an unknown age as fresh. You can keep stale data visible during a failed refresh if it is labeled as old.
Sync other tabs
When one tab changes localStorage, other same-origin tabs receive a storage event. The writing tab does not receive its own event. The event exposes key, oldValue, newValue and url.
window.addEventListener("storage", (event) => {
if (event.key === "theme") applyTheme(event.newValue);
});
Apply the theme immediately in the writing tab too. Handle null when a key is removed. A storage event is not a replacement for updating your own page's state.
Decide what belongs elsewhere
Do not put passwords, payment card numbers or secret API keys in Web Storage. Scripts on the origin can read it, including third-party code and injected scripts. Clear relevant personal data at sign-out on shared devices. Keep important work on a server or provide export because users can clear site data and private sessions discard it.
Cookies differ: matching requests send them to the server, and HttpOnly cookies cannot be read by page scripts. They are small, about 4 KB each. Web Storage stays in the browser and is script-readable. IndexedDB fits larger structured data through an asynchronous API.
Key Takeaways
- Validate parsed data and fall back when it is missing or damaged.
- Cache freshness comes from a timestamp and an app-defined age limit.
- The
storageevent reaches other same-origin tabs, not the writer. - Keep secrets and irreplaceable data out of Web Storage.
5Use location and clipboard features
Browser APIs connect a page to capabilities such as location and the clipboard. Their availability depends on the browser, the page’s security context and the user’s choices. You will detect a feature before calling it, ask for permission at a useful moment, and handle success or refusal. A complete interaction includes visible feedback and another way to finish the task, such as entering a city or copying selected text with the keyboard.
5.1 Request a location with a fallback
Geolocation is a browser API: a capability provided by the browser, separate from the JavaScript language. Like other device features, it can be unavailable or refused. A weather page should still work when a reader chooses to type a city.
Check the feature and the context
Detect the capability itself with "geolocation" in navigator. Checking a browser's name is less reliable: versions and settings change what is available. Geolocation needs a secure context, normally HTTPS; http://localhost also qualifies for development. window.isSecureContext reports whether the current page qualifies. Plain HTTP on another device's network address is not the same as localhost.
Explain why location is useful and request it when the reader clicks "Use my location". The browser manages the permission prompt. A person can allow or block it, and an earlier denial can cause a later call to fail without another prompt. Site settings are where the user changes that decision.
Read the callback result
navigator.geolocation.getCurrentPosition(success, error, options) uses callbacks. On success, position.coords contains latitude and longitude in decimal degrees and accuracy in meters. Accuracy is an estimated uncertainty radius, not extra decimal precision. On failure, code 1 means permission denied, 2 means position unavailable, and 3 means timeout.
The options control the attempt:
enableHighAccuracy: requests higher accuracy; defaultfalse.timeout: waiting limit in milliseconds; defaultInfinity.maximumAge: maximum age of an acceptable cached position; default0.
Use a finite timeout so the page has a path out of waiting. watchPosition observes updates over time and returns an ID; pass that ID to clearWatch when tracking should stop.
Wrap callbacks when using await
function getPosition(options) {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject, options);
});
}
async function useLocation() {
if (!("geolocation" in navigator)) {
showCityInput();
return;
}
try {
const { coords } = await getPosition({ timeout: 10000 });
const url = new URL("https://api.example.com/weather");
url.searchParams.set("lat", coords.latitude.toFixed(2));
url.searchParams.set("lon", coords.longitude.toFixed(2));
await loadWeather(url);
} catch {
showCityInput();
}
}
Here showCityInput displays the manual entry control and loadWeather requests and renders weather. Connect useLocation to the location button. The catch covers location failure and the weather request; either way, manual entry remains available.
Request only useful precision
A position of latitude 52.3676, longitude 4.9041 with accuracy 35 has an estimated radius of about 35 m. Rounding to 52.37 and 4.90 reduces the precision sent to the weather service. A latitude interval of 0.01° is about 1.1 km; longitude spacing varies with latitude. That regional precision suits a forecast without sharing the most detailed coordinates.
Where the Permissions API is available, navigator.permissions.query({ name: "geolocation" }) reports "granted", "denied" or "prompt". Detect and handle that API too. Its state helps tailor the interface but does not replace error handling for the location call.
Key Takeaways
- Detect Geolocation and use a secure context.
- Request location after a clear user action with a stated purpose.
- Latitude and longitude use degrees; accuracy uses meters.
- Set a finite timeout and offer manual entry on failure.
- Send only the location precision the feature needs.
- Permission state does not guarantee a usable position.
5.2 Copy text and learn another browser API
The Clipboard API lets a page copy text without asking the reader to select it first. Like Geolocation, it needs capability checks, an appropriate user action, error handling and a fallback.
Copy during the click
navigator.clipboard.writeText(text) returns a promise. Wait for it before reporting success. A secure context is required, and a denied operation can reject with NotAllowedError. Call it directly from a click handler: Firefox and Safari require a recent user interaction, while Chromium's requirements can also depend on clipboard-write permission.
copyButton.addEventListener("click", async () => {
try {
if (!navigator.clipboard?.writeText) {
throw new Error("Copy unavailable");
}
await navigator.clipboard.writeText(codeField.value);
copyButton.textContent = "Copied!";
} catch {
codeField.focus();
codeField.select();
copyButton.textContent = "Copy selected text: Ctrl+C / Cmd+C";
}
setTimeout(() => {
copyButton.textContent = "Copy";
}, 2000);
});
codeField is a text input or textarea and copyButton is the page's button. If copying fails, selecting the text gives a keyboard route: Ctrl+C on many systems or Cmd+C on macOS. The label returns to "Copy" after 2000 ms. Never show "Copied!" before the promise fulfills.
Reading uses navigator.clipboard.readText() and has tighter controls because a clipboard can contain private material from other apps. A browser can require permission or present a paste prompt. Permission to write does not establish permission to read.
Treat connectivity as a hint
navigator.onLine and the window's online and offline events help show a connection banner. They do not establish whether your API is reachable. A device can have a network connection while the API server is down or inaccessible.
window.addEventListener("offline", () => {
status.textContent = "You are offline.";
});
Continue to handle errors around fetch even when navigator.onLine is true. Use an appropriate live region for the status message.
Read a shareable search
The URL API reads the page's own query string as well as building API URLs. On https://books.example.org/search?q=dune, new URL(location.href).searchParams.get("q") returns "dune". Read that value on load to restore a search from a shared link. A missing parameter returns null, so decide what the initial screen should show then.
Use one learning method
For another browser API, read its syntax, secure-context and permission rules, compatibility table, and MDN Baseline status. Then detect the feature, call it from a user action when required, handle its promise or callbacks, and provide an alternative when it is missing or refused.
The Notifications API displays system notifications after permission; Web Share opens a device share menu where supported; Canvas draws graphics; media APIs control audio and video. A third-party mapping API may add a library and a provider key. Its credentials follow the same secret-key rules as any other service. Testing one browser over localhost does not establish that another device or browser has the same support and context.
Key Takeaways
- Wait for
writeTextbefore reporting a successful copy. - Keep a keyboard-copy fallback when clipboard access fails.
- Clipboard reading has separate, tighter permission rules.
- Online status does not establish API reachability.
- Use URL parameters to restore a shareable search.
- Check compatibility, detect features and handle refusal for each browser API.
Fetching Data and Browser APIs examples
1Build and update a reading list
14 steps
- 1IntroductionFollow one exchange
A reading-list page needs to load books and add a note about one of them. The page is the client; the API is the server. Each exchange has a request describing the action and a response describing its outcome.
We will read the exchange first, then connect the two asynchronous steps in JSON loading, and finally submit a change. The server’s reply determines what the page can safely show next.
- 2ExampleRead the resource and filters
The page requests
GET https://api.example.com/books?author=Le%20Guin&limit=5.GETasks to read./booksnames the resource. The query filters by author and limits the result count.%20represents the space in "Le Guin". The API documentation determines these parameter names; the page cannot choose a different name and expect the service to understand it. - 3Key InsightSeparate status from connection
A response can report
200 OKwith a book or404 Not Foundwith an error. Both prove that a server answered. A connection failure supplies no HTTP status to the page.Inspect the Network tab for the full URL, method, status, headers and body. If the API returned the expected JSON, the next place to inspect is the code that reads or displays it.
- 4ChallengeChoose a method for a change
A note already exists. The page should change only its
donefield while preserving its text. Which HTTP method fits that operation, and why wouldGETbe wrong?---reveal---
Use
PATCHfor the partial update.GETis a safe read method and must not be used to request a data change.PUTwould describe a complete replacement and could reset fields omitted from the body. - 5ExplanationWait for the response
Start with
const response = await fetch(url). This waits until the response headers arrive and gives aResponse, not the parsed books.The reply may still be an HTTP error.
fetchfulfills for a404because a response exists. Checkresponse.okbefore treating the operation as successful; it is true for status codes200through299. - 6ExampleCheck and read the body
Continue inside the async function:
if (!response.ok) { throw new Error(`HTTP error ${response.status}`); } const data = await response.json();The second wait reads and parses the body. Now
datais a JavaScript value. An outertry/catchcan handle both the status error thrown here and failures from fetching or parsing the body. - 7ExampleFind the nested array
Suppose the body is
{"results":[{"title":"Dune"}],"total":1}.The first step,
data.results, reaches the array.[0]reaches its first object..titlegives"Dune". A renderer should receivedata.results, not the whole outer object. The Network response preview is useful when the API’s shape differs from what the page expected. - 8ChallengeDiagnose the missing value
The code uses
const data = response.json()and then triesdata.results[0]. The body really is valid JSON with aresultsarray. What is missing?---reveal---
Wait for the body:
const data = await response.json(). Without that wait,datais a promise and has noresultsproperty. The firstawait fetch(...)did not also wait for JSON parsing. - 9TipPreserve punctuation in a search
Build query parameters through the URL API:
const url = new URL("https://api.example.com/search"); url.searchParams.set("q", "rock & roll");The value becomes
rock+%26+roll. The ampersand stays inside the search phrase instead of splitting the query. Reuse the parsed response after loading: calling a second body reader on the same response fails because the body was consumed. - 10ExamplePrepare a JSON submission
To create a note, serialize the fields and name their format:
const response = await fetch("https://api.example.com/notes", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ title: "Read", done: false }), });The method requests creation, the header declares JSON, and the body contains JSON text rather than an ordinary object.
- 11ConnectionUse the stored record
The server may return
201 Createdwith{"id":31,"title":"Read","done":false}. Add that returned record to the page’s state. Its ID lets a later action call/notes/31.For a form with files, pass
FormDatainstead of JSON and let the browser set its multipart boundary. AGETrequest cannot carry a body throughfetch; search fields belong in the query string. - 12ChallengeHandle a bodyless success
Deleting a saved note returns
204. The code checksresponse.ok, then callsresponse.json()and shows an error. Did deletion necessarily fail, and what should change?---reveal---
No:
204reports successful completion without a body. Skip JSON parsing for that status and update the page after the status check. The parse failure came from trying to read empty content, not from evidence that the delete failed. - 13ChallengeReview a complete submit flow
A save button can be clicked twice while a
POSTruns. A successful reply supplies a new ID. Name the two page behaviors that prevent an accidental duplicate and preserve that ID.---reveal---
Disable the button during the request and restore it in
finally. On success, update from the returned record, including its ID. The button prevents repeated clicks; the server result supplies values that did not exist in the submitted data. - 14RecapCheck the exchange at each boundary
When a request misbehaves, inspect the operation in order: the documented URL and method, the actual status, the chosen body reader, and the data shape handed to the page.
For a write, check the content type and serialized body too. A success without content needs no JSON reader. A create reply should supply the saved record used for the next edit or delete.
2Build a search page that stays current
18 steps
Save this lesson to follow every Walkthrough step by step.
- 1IntroductionKeep the screen tied to the search
- 2ExampleReplace the previous list
- 3Key InsightKeep zero and missing data distinct
- 4ChallengeHandle external text and links
- 5ExampleRead the time unit
- 6ExplanationChoose the display zone
- 7ExampleFormat documented minor units
- 8ChallengeSeparate locale from value
- 9ExplanationBegin with visible waiting
- 10ExampleFinish all paths
- 11ChallengeRecognize a successful empty search
- 12TipWait before a temporary retry
- 13ExampleNotice the completion order
- 14ExampleCancel an outdated request
- 15ConnectionReduce calls during typing
- 16ChallengeChoose partial success
- 17ChallengeProtect the whole current interaction
- 18RecapTest the interface with imperfect responses
3Diagnose a cross-origin API call
10 steps
Save this lesson to follow every Walkthrough step by step.
- 1IntroductionSeparate two permission questions
- 2ExampleCompare the full origins
- 3ExplanationRead the server’s permission
- 4ExampleFollow the preflight first
- 5ChallengeExplain a terminal-only success
- 6ExampleFind the exposed key
- 7ConnectionMove the secret’s boundary
- 8ChallengeDistinguish transport from secrecy
- 9ChallengeCheck cookies and readable replies
- 10RecapDiagnose the boundary that failed
4Restore a saved list without breaking the page
12 steps
Save this lesson to follow every Walkthrough step by step.
- 1IntroductionKeep a convenient local copy
- 2ExplanationChoose what should survive
- 3ExamplePreserve an array as text
- 4ChallengeRestore the intended type
- 5TipHandle a write that cannot fit
- 6ExampleCheck more than JSON syntax
- 7ExampleAttach a cache time
- 8ChallengeCalculate which cache is fresh
- 9ConnectionFollow a theme across tabs
- 10Key InsightProtect important data
- 11ChallengeRecover from an old saved format
- 12RecapTest a fresh and a returning visit
5Add useful location and copy buttons
13 steps
Save this lesson to follow every Walkthrough step by step.
- 1IntroductionMake the action understandable
- 2ExplanationCheck the context and capability
- 3TipAsk when the feature is useful
- 4ExampleSet a finite wait
- 5ExampleRead only useful precision
- 6ChallengeRecover from a refusal
- 7ExampleWrite during the click
- 8Key InsightWait before celebrating the copy
- 9ChallengeTreat a connection signal as a hint
- 10ConnectionRestore a search from its link
- 11TipCheck another browser feature
- 12ChallengeReview both buttons together
- 13RecapTest the person’s choices too
Fetching Data and Browser APIs practice questions
Set 1: Debug requests, storage and permissions10 Practice Questions
Question 1 of 10
if (localStorage.getItem("compact")). The stored text is "false", yet compact mode turns on. Why?Show answerHide answer
getItem returns text, and the nonempty string "false" is truthy in JavaScript. It does not automatically become the boolean false again. If the stored format is "true" or "false", compare the value explicitly with "true" when restoring the setting.Question 2 of 10
PUT /profiles/29 as replacing the profile. What does idempotence imply when the same complete request is sent twice?Show answerHide answer
Question 3 of 10
Show answerHide answer
Question 4 of 10
new Date and shows January 1970. What conversion should precede date formatting?Show answerHide answer
Date counts milliseconds, so multiply the documented value by 1000. A locale controls how a date is written and cannot repair the wrong time unit. Adding a zone offset also does not correct a timestamp that is smaller by a factor of 1000.Question 5 of 10
503 with Retry-After: 12. What behavior communicates the state and supports recovery?Show answerHide answer
Retry-After: 12 asks for a twelve-second delay before another attempt; retrying immediately ignores that instruction. A 503 is a service failure, not evidence that the catalog contains no items.Question 6 of 10
Show answerHide answer
Question 7 of 10
Show answerHide answer
Question 8 of 10
Show answerHide answer
Question 9 of 10
-12.0464, longitude -77.0428 and accuracy 80. What does 80 describe?Show answerHide answer
Question 10 of 10
https://recipes.example.org/search?q=green+tea reads new URL(location.href).searchParams.get("q"). What value does it get?Show answerHide answer
searchParams.get("q") retrieves and decodes the value of the named query parameter, so the plus becomes a space in "green tea". It does not return the parameter name or the raw encoded query. A missing parameter would return null, but q is present in this address.Set 2: Design reliable API features10 Practice Questions
Save this lesson to see every answer and practice every set.
Question 1 of 10
429. What does that status mean?Show answerHide answer
Save this lesson to see this answer.
Question 2 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 3 of 10
<b> text, a javascript: link, and remaining: 0. The page must show the literal title, permit only ordinary web links, and preserve the count. What rendering plan meets all three needs?Show answerHide answer
Save this lesson to see this answer.
Question 4 of 10
Intl.DateTimeFormat with a fixed en-US locale but no timeZone option. Their displayed clock times differ. What explains the difference?Show answerHide answer
Save this lesson to see this answer.
Question 5 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 6 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 7 of 10
PATCH uses credentials: "include". Preflight permits it. The final response has an allow-origin wildcard and allows credentials, so JavaScript cannot read it. The server also assumes CORS makes user checks unnecessary. What response and access policy repairs this design?Show answerHide answer
Save this lesson to see this answer.
Question 8 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 9 of 10
.map. Some users have valid JSON {"version":1,"items":[]} left by older code, and others have damaged text. What restoration plan handles both cases?Show answerHide answer
Save this lesson to see this answer.
Question 10 of 10
navigator.onLine === true, but an API call rejects. What should the page infer about connectivity and handle next?Show answerHide answer
Save this lesson to see this answer.
Set 3: Follow data through the interface10 Practice Questions
Save this lesson to see every answer and practice every set.
Question 1 of 10
fetch call returns a Response with status 502. The code has a catch handler but never checks response.ok. Why might its HTTP-error message never appear?Show answerHide answer
Save this lesson to see this answer.
Question 2 of 10
204 on success. What should the page do before attempting to parse JSON?Show answerHide answer
Save this lesson to see this answer.
Question 3 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 4 of 10
Promise.all take?Show answerHide answer
Save this lesson to see this answer.
Question 5 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 6 of 10
POST with Content-Type: application/json. No preflight permission is cached. What must the browser check before sending that POST?Show answerHide answer
Save this lesson to see this answer.
Question 7 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 8 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 9 of 10
http://localhost but its location feature is missing when opened on a phone at an HTTP network address. The app also stops working when desktop users deny location. What design addresses both failures?Show answerHide answer
Save this lesson to see this answer.
Question 10 of 10
navigator.clipboard.writeText(text). At what point should it show "Copied!"?Show answerHide answer
Save this lesson to see this answer.
Set 4: Requests, results and browser features10 Practice Questions
Save this lesson to see every answer and practice every set.
Question 1 of 10
403. What does that status tell the page?Show answerHide answer
Save this lesson to see this answer.
Question 2 of 10
200 with {"items":[{"name":"Cedar"}]}. What assignment stores the first item’s name after const response = await fetch(url)?Show answerHide answer
Save this lesson to see this answer.
Question 3 of 10
body receive when sending { label: "Read" }?Show answerHide answer
Save this lesson to see this answer.
Question 4 of 10
{ remaining: 0, owner: null }. How can the page display the remaining count while using "Unknown" for the missing owner name?Show answerHide answer
Save this lesson to see this answer.
Question 5 of 10
amount: 3785, currency: "USD" as integer minor units. What number should go into the currency formatter?Show answerHide answer
Save this lesson to see this answer.
Question 6 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 7 of 10
https://studio.example.org/edit. What is the origin of that page?Show answerHide answer
Save this lesson to see this answer.
Question 8 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 9 of 10
localStorage.setItem("visits", 8), what is the value of localStorage.getItem("visits") + 1?Show answerHide answer
Save this lesson to see this answer.
Question 10 of 10
1. What user-facing response fits the failure?Show answerHide answer
Save this lesson to see this answer.
Stuck on your own question? Homework Help
Key Terms
A
AbortController
AbortSignal
AbortSignal.timeout(4000) limits a request to four seconds of active time.Access token
API key
B
Baseline
C
Cache
Clipboard API
CORS
Cross-site scripting
textContent for API titles removes the HTML interpretation that can permit cross-site scripting.D
Debouncing
E
Empty state
Endpoint
POST to create a note and returns the new record’s identifier.Expiry
Exponential backoff
F
Fallback
Feature detection
"geolocation" in navigator decides whether the page can offer device location.FormData
G
Geolocation
H
Header
Content-Type header tells the receiving program that the accompanying body contains JSON.HTTP request
/books and includes a query parameter that limits the number of results.HTTP response
404 and explains in its body that the requested book was not found.HttpOnly
I
Idempotence
IndexedDB
Intl.NumberFormat
Intl.NumberFormat for every price instead of rebuilding a currency string for each item.J
JSON
results property before building list items.L
Live region
role="status" announces "Loading…" and the later search-result message.Loading state
Locale
en-US locale writes a date differently from en-GB even when both format the same instant.localStorage
Location accuracy
M
Minor currency unit
4680 represents 46.80 dollars.Mixed content
Multipart boundary
N
Nullish coalescing
?? only when its left-hand value is null or undefined, preserving valid zeros.O
Opaque response
Optional chaining
?. is null or undefined, returning undefined.book.author?.name handle a book whose author object is missing from the response.Origin
P
Pagination
Parse error
204 body causes a JSON parse error even though the server successfully completed the operation.Permission
Preflight
Promise.all
Promise.allSettled
Q
Query parameter
?, used by an endpoint for inputs such as searches or page limits.limit=12 asks the search endpoint to return no more than twelve results.R
Race condition
Rate limit
Rendering
Request body
Response body
Response object
200, but the page still awaits json() to obtain the book data.Retry-After
Retry-After value of 15 asks the page to wait fifteen seconds before another request.S
Same-origin policy
Secure context
Serialization
JSON.stringify preserves a note’s fields when they are sent as a JSON request body.sessionStorage
Status code
201 tells the page that the server created a resource in response to the request.Storage event
Storage quota
Synchronous operation
T
textContent
textContent displays its angle brackets literally instead of creating an HTML element.Time zone
U
Unix timestamp
URL encoding
User activation
UTC
Z at the end of an ISO date-time.W
Web API
Web Storage
About this lesson
Make your own Study Materials
Turn your notes, slides and documents into Study Guides, Practice Questions, Flashcards and 6 more tools.