completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

Requests in depth

Build precise fetch requests with HTTP methods, headers, JSON bodies, Request and Response objects, credentials, and cache options.

You will learn to
  • 01
    Pick the right methodTell which methods only read and which are safe to repeat, and use GET, POST, PUT, PATCH, and DELETE on a small todo server.
  • 02
    Send headers and JSONUse a Headers object, JSON.stringify, and the Content-Type, Accept, and Location headers.
  • 03
    Work with Request and Response objectsBuild them, clone a body before reading it twice, and choose the credentials and cache options.

What a request is made of

In the fetch lesson, a request was simple: ask a URL for data, check response.ok, and read the JSON. Real apps also save, change, and delete data. For that, a request needs more parts.

A request can say:

  • which method to use: the action you want, such as "read" or "create".
  • which headers to send: short notes about the request, such as "the data I send is JSON".
  • which body to send: the data itself, such as a new todo.
  • whether credentials may go along: the cookies and logins the browser has saved.
  • how to use the browser's cache: its store of earlier replies.

This lesson doesn't talk to a real server on the internet. A server is the computer that keeps the data and answers requests. Here, a tiny pretend server lives inside the page and keeps a list of todos in memory. The function fakeFetch(input, init) works like fetch. It makes a real Request object, hands it to the pretend server, and gives back a real Response object. The response has a status code, a JSON body, and headers such as Location (where a new todo lives) and ETag (a version label).

Real-life analogyAsking at a library desk

Picture a library help desk. What you ask for tells the librarian what kind of work to do. Asking to see a book twice does no harm. Filling in the "add this book" form twice may add the same book twice.

In real life: May I see this book?
In JavaScript: GET reads something
In real life: Please add this new book
In JavaScript: POST creates something new
In real life: Replace this book with the new edition
In JavaScript: PUT replaces the whole thing
In real life: Fix the typo on page 12
In JavaScript: PATCH changes one part
In real life: Please remove this book
In JavaScript: DELETE removes something
Real-life analogyPosting an order form

You post an order form to a shop. On the envelope you write short notes: "the form inside is JSON", "please reply in JSON", "here is my login". Those notes are the headers. The form inside is the request. The letter the shop sends back is the response.

In real life: Notes written on the envelope
In JavaScript: Headers, like Content-Type, Accept, and Authorization
In real life: The filled-in order form
In JavaScript: A Request object
In real life: The letter that comes back
In JavaScript: A Response object, with a status, headers, and a body
In real life: A photocopy made before you send the form
In JavaScript: clone(), used before you read a body twice

GET, POST, PUT, PATCH, DELETE

INTERACTIVE

An HTTP method is a word at the start of every request. It tells the server what you want to do. Each thing the server keeps, like one todo or the whole todo list, is called a resource. Each resource has its own URL, such as /api/todos/2. This style, where the URL names the resource and the method names the action, is called REST.

Two words help you describe methods:

  • Safe: the method only reads. It doesn't change anything on the server.
  • Idempotent: sending the same request many times leaves the server the same as sending it once.

These words matter when a request gets sent again. That happens when someone double-clicks a button, refreshes the page, or the network tries again.

Three methods, and whether they can be repeatedPop out in the code editor (opens in a new tab)JavaScript
const methods = [  ["PUT", "replace", true],  ["PATCH", "partial change", "depends"],  ["DELETE", "remove", true],]; for (const [method, action, repeatable] of methods) {  console.log(method, action, repeatable);}

Each row in the methods list has three parts: the method, what it does, and whether repeating it gives the same result. Line by line:

  • Line 2: PUT replaces a whole todo. Sending the same replacement twice gives the same todo, so it says true.
  • Line 3: PATCH changes part of a todo, so it says "depends". A patch that sets completed: true gives the same result every time. A patch that says "add 1 to the counter" changes the result every time.
  • Line 4: DELETE removes a todo. A second delete has nothing left to remove, so it says true.
  • Lines 7 to 9 go through the list and print one line per method, such as PUT replace true.

The table below lists the common methods. Read each row as: this method usually does this; does it only read; and does repeating it give the same result?

What each HTTP method does, and whether it is safe to repeat
MethodWhat it usually doesDoes it only read? (safe)Same result if repeated? (idempotent)Why it matters
GETReads something.Yes.Yes.Reading the same thing twice changes nothing.
HEADReads only the headers, without the body.Yes.Yes.Handy for checking a file's size or type without downloading it.
OPTIONSAsks which methods are allowed.Yes.Yes.Browsers often send it first, before some requests to another website.
POSTCreates something, or sends a form.No.No, not usually.Sending the same form twice may create two records.
PUTReplaces a whole thing.No.Yes.The same full replacement gives the same result each time.
PATCHChanges one part of a thing.No.No, not usually.Some patches give the same result each time, but PATCH doesn't promise it.
DELETERemoves a thing.No.Yes.Once it's gone, deleting it again removes nothing more.

So GET, HEAD, and OPTIONS are safe and idempotent. PUT and DELETE change data, so they are not safe. But they are idempotent. POST and PATCH are usually not idempotent: sending the same order twice can make two orders.

Left: POST /api/orders sent twice. The orders cart now holds order 1 and order 2, so there are 2 orders. Right: DELETE /api/todos/1 sent twice. The todos cart holds only todo 2 either way, so the result is the same.POST /api/orderssent twiceorder 1order 2orders2 ordersDELETE /api/todos/1sent twicetodo 2todossame result
The same POST sent twice can make two orders. The same DELETE sent twice ends the same way: todo 1 is gone.

The playground below sends requests to the pretend server. The "Server data" line shows the todo list after each request. Try these in order:

  1. GET /api/todos with no body.
  2. POST /api/todos with the "new title" body.
  3. PATCH /api/todos/2 with the "mark completed" body.
  4. DELETE /api/todos/1.
Pretend todo server
Send a new todo with POSTPop out in the code editor (opens in a new tab)JavaScript
const response = await fakeFetch("/api/todos", {  method: "POST",  headers: {    "Content-Type": "application/json",    Accept: "application/json",  },  body: JSON.stringify({ title: "Pack a snack" }),}); console.log(response.status);console.log(response.headers.get("Location"));console.log(await response.json());
The real request and responseGET
  1. ReadyPick a method, a URL, and a body. Then press Send request.
Server data[{"id":1,"title":"Read the fetch reply","completed":true,"version":1},{"id":2,"title":"Practice request methods","completed":false,"version":1}]
Try it yourself

Each Send builds a real Request, gives it to a small todo server that lives in this page, and shows the real Response. Reset puts the todos back.

Nothing is sent over the internet. The server lives in this lesson, so you get the same results every time.

One rule to know: a GET or HEAD request can't have a body. If you give it one, the Request constructor throws a TypeError.

Which method is this?
  • GET /api/todos
  • HEAD /pricing
  • PUT /api/todos/2 with the whole new todo
  • DELETE /api/todos/2
  • POST /api/orders
  • PATCH /api/todos/2 that adds 1 to a counter
Try it yourself
0 of 6 correct

Sort each request: does it only read, does it change data but give the same result when repeated, or can each repeat do more?

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Headers

LAB

Headers are short name-and-value notes sent with a request or a response. For example, Content-Type: application/json says "the body is JSON". JavaScript keeps headers in a Headers object. It has a few methods:

  • append(name, value) adds one more value to a header.
  • set(name, value) replaces every value the header had.
  • get(name) reads a header. If it has several values, you get them joined with a comma.

Header names ignore upper and lower case. Content-Type, content-type, and CONTENT-TYPE are the same header.

Add, replace, and read headersPop out in the code editor (opens in a new tab)JavaScript
const headers = new Headers();headers.append("Accept", "application/json");headers.append("accept", "text/plain");headers.set("Content-Type", "application/json"); console.log(headers.get("ACCEPT"));console.log([...headers]);

Lines 2 and 3 add two values to the same header, Accept. Line 3 writes the name in lower case, but it is still the same header. Line 4 sets Content-Type. Line 6 reads ACCEPT and prints application/json, text/plain. Line 7 prints every header as a [name, value] pair, with the names in lower case.

Left: after two append calls, the Accept header holds two values, application/json and text/plain. Right: after set, the Accept header holds one value, application/xml.append, appendadds a value each timeapplication/jsontext/plainAccept2 valuessetreplaces them allapplication/xmlAccept1 value
Two append calls give Accept two values. A later set throws both away and keeps only the new one.
Headers lab
Add, replace, and read headersPop out in the code editor (opens in a new tab)JavaScript
const headers = new Headers();headers.append("Accept", "application/json");headers.append("accept", "text/plain");headers.set("Content-Type", "application/json"); console.log(headers.get("ACCEPT"));console.log([...headers]);
What the browser didbrowser
  1. ReadyPress the button to run the lab in your browser.
Try it yourself

append adds a value, set replaces, and get ignores case. Some headers, like Host and Cookie, belong to the browser, so your code shouldn't count on setting them.

The browser-only line shows what this browser lets a Headers object store. The browser still decides what is really sent.

Some headers belong to the browser. Your code shouldn't try to set Host (the server's name) or Cookie (saved login data). The browser decides what to send for those. The lab shows what your browser lets a Headers object store. But storing a header in the object doesn't mean the browser will send it.

Let the browser label FormData

FormData holds form fields, and it can hold files. When the body is FormData, don't set Content-Type yourself. The browser sets it to multipart/form-data plus a boundary: a made-up marker that separates the fields in the body. Only the browser knows that marker. URLSearchParams works the same way: the browser labels it as form data for you.

Sending JSON

STEP THROUGH

JSON is a text format for data. To send JSON, you need three things:

  • a method that can carry a body, such as POST.
  • the header Content-Type: application/json, so the server knows the body is JSON.
  • the data turned into text with JSON.stringify.

The JSON lesson explains why JSON is text and not a live object.

A helper that sends a new todo as JSONPop out in the code editor (opens in a new tab)JavaScript
async function createTodo(title) {  const headers = new Headers();  headers.set("Accept", "application/json");  headers.set("Content-Type", "application/json");   const body = JSON.stringify({ title });  const response = await fakeFetch("/api/todos", {    method: "POST",    headers,    body,  });   if (!response.ok) {    const problem = await response.json();    throw new Error(response.status + " " + problem.message);  }   const location = response.headers.get("Location");  const todo = await response.json();  return { location, todo };}

Line by line:

  • Lines 2 to 4 make a Headers object. Accept asks for JSON back. Content-Type says the body is JSON.
  • Line 6 turns { title } into JSON text.
  • Lines 7 to 11 send it to /api/todos with POST.
  • Line 13 checks response.ok. If the server said no, line 14 reads the server's error message, and line 15 throws an Error.
  • Line 18 reads the Location header. Line 19 reads the new todo. Line 20 returns both.
A page window runs createTodo(). An arrow labelled POST /api/todos with the JSON body title Pack a snack goes to a server window, which saves todo 3. An arrow labelled 201 Created, Location: /api/todos/3 comes back to the page.pagecreateTodo()POST /api/todos{"title":"Pack a snack"}201 CreatedLocation: /api/todos/3serversaves todo 3
createTodo sends a POST with a JSON body. The server saves the todo and replies 201, with a Location header that says where the new todo lives.
Step through createTodo
Step 0 of 10Ready
Your turn: follow the blue line

Pick a title, then step through what the pretend server really did.

Running in
  1. script
Next: line 1
  const headers = new Headers();  headers.set("Accept", "application/json");  headers.set("Content-Type", "application/json");   const body = JSON.stringify({ title });  const response = await fakeFetch("/api/todos", {    method: "POST",    headers,    body,  });   if (!response.ok) {    const problem = await response.json();    throw new Error(response.status + " " + problem.message);  }   const location = response.headers.get("Location");  const todo = await response.json();  return { location, todo };}
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
Pick the title to send to the server

The title sent is "Pack a snack". Changing it starts a new replay.

A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

Pick "empty title" or "duplicate title" above the replay. The request still reaches the pretend server, but the server says no: 422 for an empty title, and 409 for a title that already exists. The helper reads the server's JSON error message and throws an Error with it.

Status codes the pretend server uses

A status code is a number in every response. It says how the request went.

  • 201 Created: the todo was made. The response has a Location header with the new todo's URL.
  • 409 Conflict: a todo with that title already exists.
  • 422 Unprocessable Content: the body was valid JSON, but the data was wrong, such as an empty title.

Request and Response objects

OBJECTS

fetch can take a URL plus an options object. It can also take a ready-made Request object. A Request holds everything about one request: the URL, the method, the headers, the body, and the other options. You can build it now and send it later.

A Response holds the server's answer: the status code, the headers, and the body.

The body of a request or a response is a stream: data that arrives in pieces and can be read only once. Sometimes you need to read a body twice, for example once to log it and once to use it. Then call clone() first. clone() makes a copy with its own body. You met the "read once" rule with response.json() in the fetch lesson. Here it applies to requests too.

Request and Response object lab
Clone a request, then build a JSON responsePop out in the code editor (opens in a new tab)JavaScript
const request = new Request("/api/todos", {  method: "POST",  headers: { "Content-Type": "application/json" },  body: JSON.stringify({ title: "Clone me" }),}); const copy = request.clone();console.log(await request.text());console.log(await copy.json()); const response = Response.json(  { saved: true },  { status: 201, headers: { Location: "/api/todos/3" } },);console.log(response.status);console.log(response.headers.get("content-type"));
What the objects didclone
  1. ReadyPick a button, then press Run object lab.
Try it yourself

A request or response body can be read only once. Clone it before reading it twice. Response.json is a short way to build a JSON response, in browsers that have it.

Browsers added Response.json in 2023, so the lab checks that it exists before using it.
Clone a request, then build a JSON responsePop out in the code editor (opens in a new tab)JavaScript
const request = new Request("/api/todos", {  method: "POST",  headers: { "Content-Type": "application/json" },  body: JSON.stringify({ title: "Clone me" }),}); const copy = request.clone();console.log(await request.text());console.log(await copy.json()); const response = Response.json(  { saved: true },  { status: 201, headers: { Location: "/api/todos/3" } },);console.log(response.status);console.log(response.headers.get("content-type"));

Line 7 clones the request before anything reads it. Line 8 reads the original as text. Line 9 reads the copy as JSON. Lines 11 to 14 build a 201 response with Response.json. Line 15 prints 201. Line 16 prints its content-type header, which is application/json.

Read the parts of a RequestPop out in the code editor (opens in a new tab)JavaScript
(async () => {const request = new Request("https://example.test/todos", {  method: "POST",  headers: { Accept: "application/json" },  body: "title=Pack%20tea",}); console.log(request.method);console.log(request.headers.get("accept"));console.log(await request.text());})();

Lines 1 and 11 wrap the code in an async function, so it can use await. Line 2 builds a POST request. Nothing is sent to a server. Line 8 prints POST. Line 9 asks for accept in lower case and still finds application/json. Line 10 reads the body as text and prints title=Pack%20tea. (%20 is how a space is written in this URL-style text.) After line 10, the body is used up.

Read status, statusText, and okPop out in the code editor (opens in a new tab)JavaScript
const missing = new Response("missing", {  status: 404,  statusText: "Not Found",});const broken = new Response("failed", { status: 500, statusText: "Server Error" }); console.log(missing.status, missing.statusText, missing.ok);console.log(broken.status, broken.statusText, broken.ok);

Lines 1 to 5 build two responses by hand: a 404, which means "not found", and a 500, which means "the server failed". Line 7 prints 404 Not Found false. Line 8 prints 500 Server Error false. ok is true only for status codes from 200 to 299. statusText is a short reason in words. Check the number, not the words.

Look inside a redirect responsePop out in the code editor (opens in a new tab)JavaScript
const response = Response.redirect("https://example.test/login", 302); console.log(response.status);console.log(response.headers.get("location"));console.log(response.redirected);

A redirect is a reply that says "look at this other URL instead". Line 1 builds a 302 redirect response with Response.redirect. Line 3 prints 302. Line 4 prints the location header, which holds the new URL. Line 5 prints false. redirected is true only when fetch really followed a redirect. This response was built by hand, so it is false.

bodyUsed tells you whether the body has been read. It starts as false. It becomes true as soon as something starts reading the body. A reader reads a body in pieces, called chunks. That lets a page handle a large reply one piece at a time.

Read a response body in chunksPop out in the code editor (opens in a new tab)JavaScript
(async () => {const response = new Response("hello");const reader = response.body.getReader(); console.log(response.bodyUsed);const first = await reader.read();console.log(new TextDecoder().decode(first.value), first.done);console.log(response.bodyUsed);console.log((await reader.read()).done);})();
  • Line 2 makes a response with the body "hello". No network is used.
  • Line 3 gets a reader for the body.
  • Line 5 prints false: nothing has been read yet.
  • Line 6 reads the first chunk. Line 7 turns its bytes back into text with TextDecoder and prints hello false. Here false means the stream isn't finished yet.
  • Line 8 prints true, because reading has started.
  • Line 9 reads again and prints true: the stream is finished.
Clone a response before reading it twicePop out in the code editor (opens in a new tab)JavaScript
(async () => {const response = new Response(JSON.stringify({ name: "Asha" }));const copy = response.clone(); console.log((await response.json()).name);console.log((await copy.json()).name);})();

Line 3 makes the copy before anything reads the body. Line 5 reads the original and prints Asha. Line 6 reads the copy and also prints Asha. Each one had its own body. Only clone when two parts of your code really need the body: the browser may have to keep a second copy of the data in memory.

A response cart holds a body card. An arrow labelled clone() leads to a copy cart that holds its own body card. Reading the response prints Asha, and reading the copy also prints Asha.bodyresponseprints Ashaclone()bodycopyprints Asha
clone() makes a copy with its own body before anything is read. Each body is read once, so both print Asha.
More options a Request can carryPop out in the code editor (opens in a new tab)JavaScript
const request = new Request("https://example.test/profile", {  method: "POST",  mode: "cors",  credentials: "include",  cache: "no-store",  redirect: "manual",  keepalive: true,  body: "name=Asha",}); console.log(request.mode, request.credentials);console.log(request.cache, request.redirect, request.keepalive);

Three of these options are new:

  • mode says which rules apply to requests to other websites. "cors" allows a request to another website only if that website agrees.
  • redirect says what to do with a redirect: follow it, fail, or hand it to your code.
  • keepalive lets a small request finish even while the page is closing.

Line 11 prints cors include. Line 12 prints no-store manual true. The Request keeps every choice. But these options don't give your code extra powers: the browser and the server still apply their own rules.

Response.json(data, init) builds a response with a JSON body. It also sets the Content-Type header for you. Browsers added it in 2023, so the lab checks that it exists before using it. For older browsers, use new Response(JSON.stringify(data), ...) instead.

credentials and cache options

REAL CHECK

Credentials are login details the browser keeps for you: cookies, HTTP logins, and client certificates. The credentials option says whether they may go with the request.

An origin is the start of a URL: its scheme, domain, and port, such as https://example.com. A request to the page's own origin is same-origin. A request to any other origin is cross-origin.

The default is "same-origin": credentials go only to the page's own origin. To send them cross-origin, you need "include", and the other server must allow it with CORS headers. CORS is the topic of the next lesson.

The table below lists the three values. Read each row as: with this value, this is what the browser sends.

The three credentials values
ValueWhat the browser does
omitSends no cookies, HTTP logins, or client certificates.
same-originThe default. Sends them only to the page's own origin.
includeSends them to other origins too. The other server must allow this with CORS headers.
Each credentials value is kept on the RequestPop out in the code editor (opens in a new tab)JavaScript
for (const credentials of ["omit", "same-origin", "include"]) {  const request = new Request("https://example.test/profile", { credentials });  console.log(credentials, request.credentials);}

The loop makes one Request per value. It prints each value twice: once from the list, and once from the request. They match, because the Request keeps what you chose. Use omit for public data. Use the default, same-origin, for normal requests to your own site. Use include only to send credentials to another origin that allows it.

The browser's HTTP cache is a store of earlier replies. When a saved reply is still good, the browser can use it instead of asking the server again. The cache option tells the browser how to use that store for one request. It doesn't make the server any faster.

Read each row of the next table as: with this value, this is how the browser uses its cache.

The cache values
ValueWhat the browser does
defaultUses the browser's normal cache rules.
no-storeDoesn't look in the cache, and doesn't save the reply there.
reloadSkips the cache and asks the server. Then saves the new reply in the cache.
no-cacheMay use a saved reply, but only after checking with the server that it is still current.
force-cacheUses a saved reply if there is one, even an old one. Asks the server only if there is none.
only-if-cachedUses only the cache and never asks the server. In browsers, it works only with mode: "same-origin".
Ask for a fresh replyPop out in the code editor (opens in a new tab)JavaScript
const request = new Request("https://example.test/stats", {  cache: "no-store",}); console.log(request.cache);

Line 1 builds a request for a stats URL. Line 2 picks no-store. Line 5 prints no-store. With this value, the browser doesn't look in its cache, and it doesn't save the reply there. It still needs the server, so this is not an offline mode.

Three more options, for quick reference. Read each row as: this option answers this question.

Other fetch options
OptionThe question it answersExample
modeWhich rules apply to requests to other websites?cors allows a request to another website only if that website agrees.
redirectWhat happens when the server says "look at another URL"?follow goes to the new URL, error fails, and manual hands the redirect to your code.
keepaliveCan a small request finish while the page closes?true helps send a short "the user left the page" message.
credentials and cache lab
Cache and credentials choicesJavaScript
await fetch(location.href, { cache: "default" });await fetch(location.href, { cache: "no-store" }); await fetch("/api/profile", { credentials: "omit" });await fetch("/api/profile", { credentials: "same-origin" });await fetch("/api/profile", { credentials: "include" });
What the browser didsame-origin
  1. ReadyPress the button to send two HEAD requests to this page.
Try it yourself

The real requests go only to this page. If default and no-store look the same, that is normal: most cache effects only show with server cache headers and repeat visits.

Sending credentials to another origin needs CORS headers from that server. The next lesson explains CORS.

redirect: "follow" is the default: fetch follows a redirect to the new URL. "error" makes fetch fail on a redirect. "manual" hands the redirect response to your code. keepalive: true is for small requests sent while the page closes, such as "the user left the page". It is not for big uploads.

The lab sends two HEAD requests to this page. HEAD asks for the headers only, without the body. One request uses cache: "default", and one uses cache: "no-store". On a page like this one, the status and headers often look the same. That is expected: a cache value is only an instruction to the browser. To see a difference, you usually need cache headers from the server, or the browser's developer tools.

Where you will use this

PRACTICAL

You'll use these parts in every app that saves data: todo lists, dashboards, profile pages, checkouts, admin tools, and search pages. Each time, you pick a method, add headers, turn the body into text, check the status, and read the body once.

  • Loading a dashboard: GET /api/stats. Add cache: "no-store" if the numbers must be fresh.
  • Creating a todo: POST /api/todos with a JSON body. The reply has a Location header.
  • Saving a whole profile form: PUT /api/profile, because the form replaces the whole saved profile.
  • Ticking one checkbox: PATCH /api/todos/2 with only { completed: true }.
  • Stopping a slow request: add an AbortController signal to the same options.

Common misconceptions

WATCH OUT
  • "POST always creates something." POST usually sends data to a list or an action. But the server decides what each URL does.
  • "Idempotent means the reply is always the same." No. It means the data on the server ends up the same. The status can change: a second DELETE may get 404, because the todo is already gone.
  • "Header names care about upper and lower case." They don't. headers.get("content-type") finds Content-Type.
  • "I should set Content-Type for every body." Set it for JSON. Don't set it for FormData; the browser does that.
  • "I can read a request body as often as I like." A body is a stream, and it can be read once. Clone first if you need two reads.
  • "credentials: include gets around CORS." It doesn't. To send credentials to another origin, that server must allow it with CORS headers.

The table below lists request parts that are easy to mix up. Read each row as: this part answers this question.

Request parts that are easy to mix up
PartThe question it answersExample
MethodWhat do I want the server to do?PATCH means change one part.
Content-TypeWhat format is the body I send?application/json means JSON text.
AcceptWhat format do I want back?application/json asks for JSON.
credentialsMay cookies and logins go along?same-origin is the default.
cacheHow should the browser use its saved replies?no-store skips the cache completely.

Practice exercises

5 EXERCISES
Exercise 1 · Warm-upPick the method that creates

You want to add a new todo to the list at /api/todos. Which method do you usually use?

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeA GET request with a body

    This code builds a GET request with a body. What error name does it print?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    try {
      new Request("https://example.test/api", {
        method: "GET",
        body: "hello",
      });
    } catch (error) {
      console.log(error.name);
    }

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · Practiceappend or set

      What two lines does this print?

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      const headers = new Headers();
      headers.append("Accept", "application/json");
      headers.append("accept", "text/plain");
      console.log(headers.get("ACCEPT"));
      headers.set("Accept", "application/xml");
      console.log(headers.get("accept"));

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeClone before reading

        What two lines does this print?

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        const request = new Request("https://example.test/api", {
          method: "POST",
          body: JSON.stringify({ title: "Clone" }),
        });
        const copy = request.clone();
        console.log(await request.text());
        console.log((await copy.json()).title);

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeFormData and Content-Type

          You send a FormData body. Should you set the Content-Type header yourself?

          Answer, then press Check. Spacing and letter case don’t matter.

            Check your understanding

            8 QUESTIONS
            Requests in depth quiz · 8 questionsScore: first tries count
            1. Question 1 of 8Which method only reads, and gives the same result when repeated?

              Choose an answer to see the explanation.

            2. Question 2 of 8What does this print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const headers = new Headers();
              headers.append("Accept", "application/json");
              headers.append("accept", "text/plain");
              console.log(headers.get("ACCEPT"));

              Choose an answer to see the explanation.

            3. Question 3 of 8Which JSON request is correct?

              Choose an answer to see the explanation.

            4. Question 4 of 8What error name does this print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              try {
                new Request("https://example.test/api", { method: "GET", body: "x" });
              } catch (error) {
                console.log(error.name);
              }

              Choose an answer to see the explanation.

            5. Question 5 of 8Why clone a Request or a Response?

              Choose an answer to see the explanation.

            6. Question 6 of 8Which credentials value does fetch use by default?

              Choose an answer to see the explanation.

            7. Question 7 of 8What should you do about Content-Type when the body is FormData?

              Choose an answer to see the explanation.

            8. Question 8 of 8What does cache: "no-store" mean?

              Choose an answer to see the explanation.

            Key takeaways

            • A method says what you want: GET reads, POST sends something new, PUT replaces, PATCH changes one part, and DELETE removes.
            • GET, HEAD, and OPTIONS are safe: they only read. GET, HEAD, OPTIONS, PUT, and DELETE are idempotent: repeating them leaves the same result.
            • Header names ignore case. set replaces a header's values; append adds one more value.
            • To send JSON, use a method that can carry a body, set Content-Type: application/json, and use JSON.stringify.
            • A request or response body can be read only once. Call clone() before reading it twice.
            • credentials is same-origin by default. The cache option tells the browser how to use its saved replies.

            Final definition: A full fetch request has a method, a URL, headers, a body, and credentials and cache settings. The answer is a Response. Its status, headers, and read-once body tell you what the server said.

            Up next: CORS & cross-origin requests.

            CompleteFrontend Clear concepts. Working examples.