completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

fetch & JSON

Request JSON with fetch, inspect Response status, parse response bodies safely, and tell HTTP errors apart from network failures.

You will learn to
  • 01
    Read an HTTP responseName the parts of a request and a response: method, URL, headers, body, status, and response.ok.
  • 02
    Read JSON safelyCall response.json() only when there is a JSON body to read.
  • 03
    Handle the right failureTell a bad HTTP status apart from a fetch that rejects.

Requests, replies, and fetch

Web pages often need data from a server: a user's profile, a list of products, or today's weather. The page asks for the data, and the server sends it back. fetch is the browser function that does the asking.

You give fetch an address, called a URL, such as /api/users/42. You can also give it some settings. fetch sends a request to that address. The server sends back a response.

The trip over the network takes time, so fetch can't give you the response straight away. It gives you a Promise: an object that will hold the result later. You wait for it with await. When it is ready, you get a Response object.

The most common job is to ask a server for JSON. JSON is data written as text, like {"name":"Ada"}. The job has three steps:

  1. Wait for the Response with await fetch(url).
  2. Check whether the server says the request worked.
  3. Wait again, with await response.json(), to read the JSON and turn it into a JavaScript object.
Real-life analogyfetch is mailing a letter

You send a letter by post. The address on the front says where it goes. Notes on the envelope tell the reader how to handle it. The letter inside is what you want to say. The post office gives you a tracking slip. Later a reply arrives, with a stamp that says how things went. You still have to open it to read it.

In real life: The letter you send
In JavaScript: The request: the method, the URL, the headers, and the body
In real life: The tracking slip from the post office
In JavaScript: The Promise that fetch returns
In real life: The reply in its envelope
In JavaScript: The Response object
In real life: Opening the envelope to read the reply
In JavaScript: A body method, such as response.json()

A Promise resolves when it finishes with a result. It rejects when it finishes with an error. Here is the most important fact in this lesson: the Promise from fetch resolves whenever the server replies. It resolves even when the reply says "404 Not Found". It rejects only when no reply reaches your code, for example when you are offline.

How HTTP works

MAP

HTTP is the set of rules that browsers and servers use to talk. Web pages, JSON data, and images all travel with HTTP. The main rule is simple: the browser sends one request, and the server sends back one response.

A page window calls fetch(url). An arrow labelled request, GET /api/users/42, goes to a server window. An arrow labelled response, 200 OK plus JSON, comes back to the page.pagefetch(url)requestGET /api/users/42200 OK + JSONresponseserverfinds user 42
The page sends one request to the server. The server sends back one response.

Every request has a method. The method says what you want. GET means "give me this". POST means "here is something new to save".

A request and a response have the same three parts. Read the table one row at a time: each row is one part, first in the request, then in the response.

The three parts of a request and a response
PartIn the requestIn the response
First lineThe method and the address. GET /api/users/42 means "give me user 42".The status code. 200 OK means it worked. 404 Not Found means nothing is at that address.
HeadersExtra notes about the request, such as Content-Type, which says what kind of data the body is.Extra notes about the response, such as Content-Type: application/json.
BodyThe data you send, if any. GET and HEAD requests can't have a body.The data that comes back: JSON, HTML, plain text, a file, or nothing at all.

The status code is a three-digit number. Its first digit tells you its group. The table below lists each group, what it means, and codes you will see often.

Status code groups
GroupWhat it meansCodes you will see
1xxInformation: the request is still going.You rarely see these when you use fetch.
2xxSuccess: it worked.200 OK, 201 Created, 204 No Content
3xxLook somewhere else. A redirect says "try this other address". 304 says "your saved copy is still good".301, 302, 304. fetch follows redirects for you.
4xxA problem with the request, on your side.400, 401, 403, 404, 409, 422, 429
5xxA problem on the server.500, 502, 503

In JavaScript, a Response object shows these parts as properties:

  • status: the status code, such as 404.
  • statusText: the words after the code, such as Not Found. Newer versions of HTTP often leave it empty, so don't rely on it.
  • ok: true when the status is from 200 to 299.
  • headers: the extra notes.
  • url: the final address the response came from.
  • redirected: true when fetch followed a redirect to get here.
  • type: a word that says how the response was fetched, such as "basic" for your own site.

You can make a Response yourself with new Response(body, options). That is handy for practice, because you don't need a server. The code below makes one with status 201 and one header.

Read the status and headers of a ResponsePop out in the code editor (opens in a new tab)JavaScript
const response = new Response("hello", {
  status: 201,
  statusText: "Created",
  headers: { "X-Request-Id": "abc-123" },
});

console.log(response.status);
console.log(response.headers.get("x-request-id"));
console.log(response.headers.get("content-type"));

The three lines print 201, abc-123, and text/plain;charset=UTF-8. Line 9 asks for content-type, which the code never set. The body is a string, so Response adds a plain-text type by itself. A header that is really missing gives null.

fetch basics

CODE

The shortest form is await fetch(url). With no settings, fetch:

  • uses the GET method,
  • follows redirects for you,
  • sends cookies only to your own site. A cookie is a small piece of data the browser keeps for a site, such as who is logged in. This setting is called credentials: "same-origin".

"Your own site" means the same origin: the same scheme, domain, and port as the page, such as https://example.com. The next lessons cover request settings, caching, and CORS.

A careful fetch function, step by stepPop out in the code editor (opens in a new tab)JavaScript
async function carefulFetch(url) {  try {    const response = await fakeFetch(url);    console.log("resolved", response.ok, response.status);     if (response.status === 204) {      console.log("no body to parse");      return null;    }     const type = response.headers.get("content-type") || "";    if (!type.includes("application/json")) {      throw new Error("Expected JSON");    }     const data = await response.json();    if (!response.ok) {      throw new HttpError(response.status, data.message);    }    return data;  } catch (error) {    console.log("caught", error.name);    throw error;  }}

fakeFetch stands in for fetch on this page; the note below explains why. HttpError is an error class this lesson made: an Error that also holds the status. Read the function in this order:

  1. Line 3 waits for the Response.
  2. Line 6 checks for status 204, which means "no content". There is no body to read, so the function stops there.
  3. Lines 11 to 14 check the Content-Type header. If the server didn't send JSON, the function throws an error.
  4. Line 16 waits a second time, to read the body as JSON.
  5. Lines 17 to 19 handle a bad status. The function throws an HttpError, so the code that called it knows something went wrong.
  6. Line 21 catches any error, prints its name, and throws it again for the caller.
This page uses a pretend server

This course runs as a plain website, and it must not call other people's servers. So the experiments use fakeFetch: a tiny pretend server inside the page. It waits a moment, then returns a real Response object. The page also runs a few real fetch calls that are safe: to this site, and to data: addresses that hold their data inside the address itself.

Headers are like a list of named notes. Read one with response.headers.get(name). You get the value as a string, or null if that header is missing. Header names ignore upper and lower case.

Read a response headerPop out in the code editor (opens in a new tab)JavaScript
const response = new Response("tea", {
  headers: { "Content-Type": "text/plain; charset=utf-8" },
});

console.log(response.headers.get("content-type"));

Line 2 writes the name as Content-Type, with capitals. Line 5 asks for content-type, all lower case, and still finds it. It prints text/plain; charset=utf-8.

Search boxes and filters send the user's words in the URL, after a ?. This part of the URL is called the query string. Don't glue it together yourself. Spaces and symbols such as & have special meanings in a URL. URLSearchParams writes them safely for you.

Build a query stringPop out in the code editor (opens in a new tab)JavaScript
const params = new URLSearchParams({
  q: "green tea",
  page: "2",
});

console.log("/api/search?" + params);

Lines 1 to 4 make two named values, q and page. Line 6 turns them into text and adds it after ?. It prints /api/search?q=green+tea&page=2. The space became a +.

Add the query string to a URLPop out in the code editor (opens in a new tab)JavaScript
const params = new URLSearchParams({ q: "tea & cake" });
const url = new URL("https://example.test/api/search");
url.search = params;

console.log(url.href);

Line 2 makes a URL object from a full address. Line 3 puts the query string into its search part. Line 5 prints https://example.test/api/search?q=tea+%26+cake. The & in tea & cake became %26, so it stays part of the search words. A plain & would start a new value.

response.ok and status

INTERACTIVE

response.ok is true or false. It is true when the status is from 200 to 299, and false for every other status. That is all it checks. It doesn't check that the body is valid JSON. It doesn't check that the data is what your app wanted.

Status code switchboard
Careful fetch functionPop out in the code editor (opens in a new tab)JavaScript
async function carefulFetch(url) {  try {    const response = await fakeFetch(url);    console.log("resolved", response.ok, response.status);     if (response.status === 204) {      console.log("no body to parse");      return null;    }     const type = response.headers.get("content-type") || "";    if (!type.includes("application/json")) {      throw new Error("Expected JSON");    }     const data = await response.json();    if (!response.ok) {      throw new HttpError(response.status, data.message);    }    return data;  } catch (error) {    console.log("caught", error.name);    throw error;  }}
Run logok
  1. 1Pick a server answer and press Run. The same careful function handles every answer.
Try it yourself

Every answer is a real Response object. 404 and 500 still resolve. Only the network error rejects, because no Response exists.

A tiny pretend server inside the page returns real Response objects after a short wait. It never contacts another site.

Pick 404, then 500, and press Run. The fetch Promise still resolves, because the server replied. Your code decides what happens next. A common choice is to read the error message from the JSON body, then throw a custom error that holds the status.

response.json()

STEP THROUGH

When fetch resolves, the body may still be arriving. So response.json() returns another Promise, and you await it a second time. It reads the whole body and turns the JSON text into a JavaScript object.

Wait 1: await fetch(url) gives a response cart holding status 200 and ok true. An arrow labelled read the body leads to wait 2: await response.json() gives the object id 42, name Ada.1. await fetch(url)status: 200ok: trueresponseread the body2. await response.json(){ id: 42, name: "Ada" }
The first await gives you the Response, with its status. The second await reads the body and gives you the data.

response.json() can reject even when fetch worked:

  • The body is not valid JSON. It rejects with SyntaxError.
  • There is no body, as with status 204. There is nothing to read, so it also rejects with SyntaxError.
  • The body was already read. It rejects with TypeError.
Real-life analogyjson() opens the envelope

A letter arrives. Holding the envelope is not the same as reading the letter. You still have to open it. When you do, the handwriting might be unreadable. Or the envelope might be empty.

In real life: The envelope arrives
In JavaScript: fetch resolves with a Response
In real life: You open it and read the pages
In JavaScript: response.json() reads the body
In real life: The handwriting can't be read
In JavaScript: Invalid JSON rejects with SyntaxError
In real life: The envelope is empty
In JavaScript: A 204 response has no body to read

Step through loadUser below. It asks the pretend server for user 42 and returns the user's name. Try each server answer:

  • With 200, the function reaches line 9 and returns "Ada".
  • With 404, fetch resolves, but the function stops at line 5 and throws.
  • With a network error, fetch rejects, so the function stops at line 2.
Step through loadUser
Step 0 of 5Ready
Your turn: follow the blue line

Pick a server answer, then step through to see where the function stops.

Running in
  1. script
Next: line 1
  const response = await fakeFetch("/api/users/" + id);   if (!response.ok) {    throw new HttpError(response.status, "User not found");  }   const user = await response.json();  return user.name;}
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
Pick the pretend server's answer

Changing the answer starts the replay again. The answers are the same ones the switchboard above uses.

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.

A body can be read only once. The first read uses it up, and response.bodyUsed becomes true. A second read rejects with TypeError.

A response cart holds one body card. The first read gives the object ok true. The second read gives a TypeError, because the body is used up.bodyresponse1st read{ ok: true }2nd readTypeError
The first json() reads the body and uses it up. The second json() has nothing left to read, so it rejects.

Try both buttons in the demo: read twice fails, and clone first works.

Body can be read once
const response = new Response(JSON.stringify({ ok: true }), {  headers: { "Content-Type": "application/json" },}); const first = await response.json();console.log(first.ok); const second = await response.json();console.log(second.ok);
Body logtwice
  1. 1Run the demo to see bodyUsed change.
Try it yourself

A Response body can be read once. After json(), text(), blob(), arrayBuffer(), or formData() reads it, bodyUsed becomes true. If two parts of your code need the body, clone the response before the first read.

The demo uses a real Response object made in the page.

Every body method follows the same one-read rule. Each one reads the body in a different form:

  • json() reads JSON and gives an object.
  • text() reads plain text or HTML and gives a string.
  • blob() reads a file, such as an image.
  • arrayBuffer() reads raw bytes.
  • formData() reads form fields.

If two parts of your code both need the body, call response.clone() before either one reads it. The copy gets its own body.

Pick the method that matches the data. For plain text, use text(). Calling json() on plain text would reject, because the text is not JSON.

Read plain textPop out in the code editor (opens in a new tab)JavaScript
const response = new Response("plain text");

console.log(await response.text());

The body is the text plain text, so line 3 prints exactly that. After this read, the body is used up. Calling json() on the same response now would reject.

Invalid JSON makes json() rejectPop out in the code editor (opens in a new tab)JavaScript
const response = new Response("{ bad", {
  headers: { "Content-Type": "application/json" },
});

response.json().catch((error) => console.log(error.name));

The Response is fine, but its body, { bad, is not valid JSON. .catch() runs a function when a Promise rejects. Here, line 5 prints SyntaxError. This is a different failure from a network error, where fetch itself rejects.

Real requests in your browser

REAL FETCH

The pretend server gives the same result every time, which is good for learning. But fetch is a real browser tool, so let's run it for real. These four requests are safe, and none of them contacts another site:

  • this page,
  • a page on this site that doesn't exist,
  • a data: URL, which holds its JSON inside the address itself, so no server is needed,
  • a cancelled blob URL. A blob URL is an address for data kept in your browser's memory. Once it is cancelled, it points at nothing.
Real requests in your browser
Safe real requestsPop out in the code editor (opens in a new tab)JavaScript
await fetch(location.href);await fetch("/javascript/this-page-does-not-exist");await fetch('data:application/json,{"name":"Ada"}');const url = URL.createObjectURL(new Blob(["gone"]));URL.revokeObjectURL(url);await fetch(url);
Browser logreal fetch
  1. 1Press a button to run a real browser fetch.
Try it yourself

These are real fetch calls that are safe on this site: this page, a missing page on this site, a data URL, and a cancelled blob URL. None of them contacts another site.

No other sites are contacted. Error messages differ between browsers, so only the error name is shown.

What each button shows:

  • fetch this page: a response can hold HTML, not JSON.
  • missing page (404): a missing page gives a response with ok: false.
  • data URL JSON: json() can read JSON with no server at all.
  • cancelled blob URL: when nothing can answer, fetch rejects with TypeError.

Network errors vs HTTP errors

SORT

Many people think every bad status makes fetch reject. It doesn't. A response with 404, 429, or 500 is still a response. These are HTTP errors: the server replied, and the reply says something went wrong.

fetch rejects only when there is no response your code can use. These are network errors:

  • You are offline, or the server can't be found.
  • The URL is not valid.
  • The browser blocks the response because of CORS, a safety rule explained below.
  • The request is cancelled, which is also called aborted. fetch then rejects with the reason given for the cancel. Often that is an AbortError.
Left: the server replies 404. fetch gives a response cart with status 404 and ok false, so fetch resolves. Right: no reply arrives. fetch gives a TypeError, so fetch rejects.server replies 404status: 404ok: falseresponsefetch resolvesno reply arrivesTypeErrorfetch rejects
A 404 is still a reply, so fetch resolves and ok is false. Only when no reply arrives does fetch reject.
Rejects or resolves?
  • A 404 response with a JSON error message
  • A 500 server error response
  • No internet, or the server can't be found
  • The browser blocks it because of CORS
  • An AbortController cancels it
  • A 200 response whose body is { nope
Try it yourself
0 of 6 correct

Put each case in its group: fetch resolves, fetch rejects, or json() rejects.

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

CORS is a browser safety rule. It decides whether a page may read responses from another site. You'll learn it two lessons from now. For now, remember one thing: when the browser blocks a response from another site, your code can't see its status or body. fetch rejects with TypeError.

A timeout also makes fetch reject. A timeout is a time limit. AbortSignal.timeout(1000) makes a signal: an object that cancels a request when it fires. This one fires by itself after 1000 milliseconds, which is 1 second. So the request can't wait forever.

Make a signal that fires after 1 secondPop out in the code editor (opens in a new tab)JavaScript
const signal = AbortSignal.timeout(1000);

console.log(signal.aborted);
console.log(signal instanceof AbortSignal);

The signal has not fired yet, so line 3 prints false. Line 4 prints true, because it is an AbortSignal. You can pass it to fetch as the signal setting. The cancellation lesson shows how to combine signals and handle the timeout error.

Put the signal in the fetch settingsPop out in the code editor (opens in a new tab)JavaScript
const signal = AbortSignal.timeout(1000);
const options = { signal };

console.log(options.signal === signal);
console.log(signal.aborted);

Line 2 puts the signal into a settings object. Making this object doesn't send anything. It only holds the signal, ready for fetch(url, options). Line 4 prints true, because it is the same signal. Line 5 prints false, because a second hasn't passed yet.

Where you will use this

PRACTICAL

Pages fetch JSON whenever they need fresh data. For example, when they:

  • load a user's profile,
  • save a to-do item,
  • search while the user types,
  • refresh a dashboard,
  • send a contact form.

Pages read JSON often, and they send it often too.

A small helper function keeps the status check in one place. This one takes the fetch function as a parameter. In a test, you can pass a stand-in that returns a Response you made yourself. Then the test gets the same result every time, with no live server.

A reusable getJSON helperPop out in the code editor (opens in a new tab)JavaScript
async function getJSON(url, fetcher = fetch) {
  const response = await fetcher(url);
  if (!response.ok) {
    throw new Error("HTTP " + response.status);
  }
  return response.json();
}

const fetcher = () => Promise.resolve(new Response(
  JSON.stringify({ name: "Asha" }),
  { headers: { "Content-Type": "application/json" } },
));

console.log((await getJSON("/api/user", fetcher)).name);

Line 2 waits for the response. Lines 3 to 5 throw an error if the status is not OK. Only then does line 6 read the JSON. Lines 9 to 12 make a stand-in fetcher that returns a real Response holding {"name":"Asha"}. So line 14 prints Asha.

Send JSON to a serverPop out in the code editor (opens in a new tab)JavaScript
const todo = { title: "Learn fetch", done: false }; await fetch("/api/todos", {  method: "POST",  headers: { "Content-Type": "application/json" },  body: JSON.stringify(todo),});

A JSON request body is text. To send JSON:

  • Use a method that allows a body, such as POST, PUT, or PATCH (line 4). GET requests can't have a body.
  • Set the Content-Type header to application/json (line 5). It tells the server how to read the body.
  • Turn your object into JSON text with JSON.stringify(todo) (line 6).
Build a JSON request without sending itPop out in the code editor (opens in a new tab)JavaScript
const todo = { title: "Buy tea", done: false };
const request = new Request("/api/todos", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify(todo),
});

console.log(request.method);
console.log(request.headers.get("content-type"));
console.log(await request.text());

new Request() takes the same address and settings as fetch, but it only builds the request. It doesn't send it. Lines 8 to 10 print POST, application/json, and {"title":"Buy tea","done":false}. The body really is text.

One helper for every request

Real apps often put all of this into one helper function. It does these steps in order:

  1. Wait for fetch, and handle a network error.
  2. Check response.ok.
  3. Skip reading when the status is 204.
  4. Read JSON only when the response should have it.
  5. For a bad status, throw a custom error that holds the status and the data.

Common misconceptions

WATCH OUT
  • "fetch rejects on 404." No. It resolves with a response whose ok is false.
  • "response.json is the JSON." No. It is a method. Calling it returns a Promise for the data.
  • "ok means my app is happy." No. It only means the status is from 200 to 299.
  • "Every response has JSON." No. A response can hold HTML, a file, invalid text, or nothing at all, as with 204.
  • "I can read the body again and again." No. A body can be read once. Call clone() first if you need two reads.
  • "Header names care about upper and lower case." No. headers.get("content-type") finds Content-Type.

The table compares three Promises that people often mix up. Read across a row to see when that Promise resolves, and why it usually rejects.

Three Promises that people mix up
Which PromiseWhen does it resolve?Why does it usually reject?
fetch(url)When the status and headers of the response arrive.A network problem, a CORS block, a bad URL, or a cancel.
response.json()When the whole body is read and turned into an object.The JSON is invalid, the body is empty, or the body was already read.
Your own helper, such as getJSONWhen it returns the data.Any error you choose to throw, such as an HttpError.

Practice exercises

5 EXERCISES
Exercise 1 · Warm-upPredict ok and status

What two values does this code print?

Starter codePop out in the code editor (opens in a new tab)JavaScript
const response = new Response("{}", { status: 404 });
console.log(response.ok);
console.log(response.status);

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

    Exercise 2 · PracticeSkip an empty body

    Your helper crashes when the server replies 204 No Content. Which Response property should you check before you read the body?

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

      Exercise 3 · PracticeName the invalid JSON error

      What error name does the .catch() print?

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      const response = new Response("not json", {
        headers: { "Content-Type": "application/json" },
      });
      response.json().catch((error) => console.log(error.name));

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

        Exercise 4 · PracticeRead the body twice safely

        This code copies the response, then reads both. What two values print?

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        const response = new Response(JSON.stringify({ ok: true }));
        const copy = response.clone();
        console.log((await response.json()).ok);
        console.log((await copy.json()).ok);

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

          Exercise 5 · ChallengeSend JSON to a server

          Which header tells the server that the request body is JSON?

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          const todo = { title: "Learn fetch", done: false };
          
          await fetch("/api/todos", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify(todo),
          });

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

            Check your understanding

            10 QUESTIONS
            fetch & JSON quiz · 10 questionsScore: first tries count
            1. Question 1 of 10When does the Promise from fetch resolve?

              Choose an answer to see the explanation.

            2. Question 2 of 10What does this print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const response = new Response("{}", { status: 404 });
              console.log(response.ok);

              Choose an answer to see the explanation.

            3. Question 3 of 10The server replies with status 204. What should your code do before it calls response.json()?

              Choose an answer to see the explanation.

            4. Question 4 of 10The body is not valid JSON. What does this print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const response = new Response("not json");
              response.json().catch((error) => console.log(error.name));

              Choose an answer to see the explanation.

            5. Question 5 of 10Which request sends JSON correctly?

              Choose an answer to see the explanation.

            6. Question 6 of 10What happens if you call response.json() twice on the same response?

              Choose an answer to see the explanation.

            7. Question 7 of 10Which is true for browser fetch when you pass no settings?

              Choose an answer to see the explanation.

            8. Question 8 of 10How should you add a search term to a fetch URL?

              Choose an answer to see the explanation.

            9. Question 9 of 10What does AbortSignal.timeout(1000) give you?

              Choose an answer to see the explanation.

            10. Question 10 of 10What should a getJSON helper do before it calls response.json()?

              Choose an answer to see the explanation.

            Key takeaways

            • fetch(url, options) returns a Promise. It resolves with a Response when the status and headers arrive.
            • A bad status such as 404 or 500 doesn't make fetch reject. Check response.ok and response.status yourself.
            • response.json() is a second wait. It reads the body once, and it can reject.
            • fetch rejects only when no usable response arrives: a network problem, a CORS block, a bad URL, or a cancel.
            • To send JSON, use a method such as POST, set Content-Type: application/json, and send JSON.stringify(data).

            Final definition: fetch sends an HTTP request and resolves with a Response. Your code checks the status, then reads the body once with the right method, such as json().

            Up next: Requests in depth. You'll learn more about methods, headers, bodies, and the Request and Response objects.

            CompleteFrontend Clear concepts. Working examples.