Node js fetch error

Node js fetch error Reading time В· 3 min ReferenceError: fetch is not defined in NodeJs # The «ReferenceError: fetch is not defined» occurs when the fetch() method is used in an environment where it’s not supported — most commonly Node.js. To solve the error, install and import the node-fetch package, which provides a […]

Содержание

  1. Node js fetch error
  2. ReferenceError: fetch is not defined in NodeJs #
  3. ReferenceError fetch is not defined in NodeJs (older versions) #
  4. Введение в fetch
  5. Простой Fetch запрос
  6. XMLHttpRequest
  7. Fetch
  8. Метаданные ответа
  9. Типы ответа
  10. Цепочки Promises
  11. POST запрос
  12. Посылаем учётные данные через Fetch-запрос
  13. How to fix ‘ReferenceError: fetch is not defined’ in Node.js
  14. Table of Contents
  15. Replicating the issue
  16. Understanding the issue
  17. Fixing the issue
  18. Upgrading Node.js to v18 or later
  19. Using node-fetch library
  20. Brian Cline
  21. So, how do we use node-fetch then?
  22. Wrapping it Up
  23. Using the Fetch API
  24. Supplying request options
  25. Sending a request with credentials included
  26. Uploading JSON data
  27. Uploading a file
  28. Uploading multiple files
  29. Processing a text file line by line
  30. Checking that the fetch was successful
  31. Supplying your own request object
  32. Headers
  33. Guard
  34. Response objects
  35. Feature detection

Node js fetch error

Reading time В· 3 min

ReferenceError: fetch is not defined in NodeJs #

The «ReferenceError: fetch is not defined» occurs when the fetch() method is used in an environment where it’s not supported — most commonly Node.js.

To solve the error, install and import the node-fetch package, which provides a fetch() compatible API in the Node.js runtime.

Note that the global fetch variable is available in Node.js starting at version 18.

You can check your version of Node.js with the node -v command.

If you use a Node.js version older than 18 , you can either download and install the LTS version from nodejs.org or install and use the node-fetch package.

If your project doesn’t have a package.json file, create one in your project’s root directory:

Now install the node-fetch library by running the following command.

Now you can import and use the module just like you would use the fetch() method in the browser.

At the time of writing, to use ES6 module imports and exports in a NodeJs project, you have to set the type property to module in your package.json file:

If I run my NodeJs script, I get the result from calling the API.

ReferenceError fetch is not defined in NodeJs (older versions) #

Only do this if you use an older NodeJs version and want to use the require syntax instead of import/export .

We installed version 2 of the node-fetch package.

Make sure you don’t have the type property set to module in your package.json file.

Now you can import the fetch package using the older require function.

We had to install version 2 of the node-fetch package to be able to use the require syntax in our NodeJs application.

It’s best to stay consistent with imports between your client and server-side code. However, this approach gets the job done if you have to support an older version of NodeJs.

Источник

Введение в fetch

fetch() позволяет вам делать запросы, схожие с XMLHttpRequest (XHR). Основное отличие заключается в том, что Fetch API использует Promises (Обещания), которые позволяют использовать более простое и чистое API, избегать катастрофического количества callback’ов и необходимости помнить API для XMLHttpRequest.

Fetch API стал доступен пользователям вместе с Service Worker’ами в global скоупе в Chrome 40, однако уже в версии 42 он станет доступен в скоупе window. Разумеется, для всех остальных браузеров, которые пока ещё не поддерживают fetch существует полифил от GitHub, который доступен уже сегодня.

Простой Fetch запрос

Давайте начнём со сравнения простого примера, реализованного с XMLHttpRequest и fetch . Всё, что мы будем делать в этом примере — сделаем запрос на URL, получим ответ и распарсим его как JSON.

XMLHttpRequest

Пример с XMLHttpRequest потребует от нас установить два обработчика событий на success и error , а так же вызвать два метода: open() и send() . Пример из MDN документации:

Fetch

Наш fetch запрос будет выглядеть так:

Первым делом мы проверяем статус ответа и проверяем, успешно ли выполнился запрос (ожидаем 200 статус). Если всё хорошо, то парсим ответ как JSON.

Ответом fetch() является Stream-объект. Это означает, что в результате вызова метода json() мы получим Promise, т.к. чтение из подобного объекта является асинхронным.

Метаданные ответа

В предыдущем примере мы изучили, как можно проверить статус объекта ответа и конвентировать сам ответ в JSON. Остальные метаданные, к которым вы возможно получить доступ (например, заголовки), приведены ниже:

Типы ответа

Когда мы делаем fetch-запрос, ответу будет дан тип «basic», «cors» или «opaque». Эти «типы» указывают на то, с какого ресурса пришли данные и могут быть использованы для того, чтобы определить процесс обработки данных.

Когда запрос сделан на ресурс, находящимся на том же origin (имеется ввиду, что запрос выполняется в рамках одного сайта. прим. пер.), ответ будет содержать тип «базовый» и для такого запроса не будет никаких ограничений.

Если запрос сделан с одного origin’а на другой (кроссдоменный запрос), который, в свою очередь, вернул CORS заголовки, тогда типом будет являться «cors». Объекты с типами «cors» и «basic» почти идентичны, однако «cors» несколько ограничивает метаданные, к которым может быть получен доступ до «Cache-Control», «Content-Language», «Content-Type», «Expires», «Last-Modified», и «Pragma».

Что касается «opaque» — то он приходит в случаях, когда выполняется CORS запрос, но удаленный ресурс не возвращает CORS заголовки. Данный тип запроса не предоставляет доступ данным или заголовку статуса, поэтому мы не имеем возможности судить о результате выполнения запроса. В рамках текущей имплементации fetch() не представляется возможности выполнять CORS запросы из скоупа window, и вот здесь написано почему. Эта функциональность должна быть добавлена, как только Cache API станет доступным из объекта window.

Вы можете определить ожидаемый режим запроса, тем самым фильтруя результаты запросов с неподходящим типом. Режим запроса может быть установлен на следующий:

— “same-origin” успешно выполняется только для запросов на тот же самый origin, все остальные запросы будут отклонены.
— “cors” работает так же, как «same-origin» + добавляет возможность создавать запросы к сторонним сайтам, если они возвращают соответствующие CORS заголовки.
— “cors-with-forced-preflight” работает так же, как «cors», но перед запросом всегда отсылает тестовый запрос на проверку.
— “no-cors” используется, когда необходимо выполнить запрос к origin, который не отсылает CORS заголовки и результатом выполнения является объект с типом «opaque». Как говорилось выше, в данный момент это невозможно в скоупе window.

Чтобы определить режим запроса, добавьте объект опций вторым параметром к запросу и установите «mode» в этом объекте:

Цепочки Promises

Одной из прекрасных особенностей Promise’ов является возможность группировать их в цепочки. Если говорить о них в скоупе fetch() , то они позволяют нам «шарить» логику между запросами.

Если вы работаете с JSON API, вам потребуется проверить статус и распарсить JSON для каждого ответа. Вы можете упростить свой код, определив парсинг статуса и JSON как раздельные функции, которые вернут Promise’ы. Вам останется подумать только об обработке самих данных и, разумеется, исключений.

Мы определяем функцию, которая проверяет response.status и возвращает результат: Promise.resolve() или Promise.reject() . Это первый вызванный метод в нашей цепочке, и если он успешно завершается( Promise.resolve() ), то вызывается следующий за ним метод — fetch() , который, в свою очередь, опять возвращает Promise от response.json() . После этого вызова, в случае удачного выполнения, у нас будет готовый JSON объект. Если парсинг провалится, Promise будет отменен и сработает условие возникновения исключения.

Но самое лучшее здесь — это возможность переиспользовать такой код для всех fetch-запросов в приложении. Такой код проще поддерживать, читать и тестировать.

POST запрос

Уже давно никого не удивишь необходимостью использовать POST метод с передачей параметров в «теле» запроса для работы с API.
Чтобы осуществить такой запрос, мы должны указать соответствующие параметры в объекте настроек fetch() :

Посылаем учётные данные через Fetch-запрос

Если вы хотите отправить запрос с каким-либо учётными данными (например, с cookie), вам следует установить `credentials` в опциях запроса на «include»:

Могу ли я отменить fetch() запрос?
В настоящий момент это невозможно, но это активно обсуждается на GitHub

Существует ли полифил?
Да

Почему «no-cors» реализован для service workers, но не для window?
Это было сделано из соображений безопасности. Подробнее можно ознакомиться здесь.

Источник

How to fix ‘ReferenceError: fetch is not defined’ in Node.js

Table of Contents

Have you been a front-end developer and recently started using Node.js? Have you used fetch to get the data from an API in Node.js the way you do in the front-end? Then most likely you would have encountered the following error:

Replicating the issue

First, let’s replicate the issue. You can update the index.js to the following and run node index.js , you should be able to see the error.

Understanding the issue

Why does the above code work perfectly fine in the front-end (or browser) and fails in Node.js? This is because fetch is a Web API and it is not supported in the version of the Node.js installed on your machine.

Fixing the issue

There are 2 ways in which you can fix this issue:

Upgrading Node.js to v18 or later

Starting version 18, Node.js has started supporting fetch API. You can download the latest Node.js version from here and install it.

Using node-fetch library

Previous to the release of Node.js v18, the most popular way to use fetch in Node.js is to install the node-fetch library.

Let’s go ahead and do it.

Now update the index.js to import fetch :

Note that we have used the import syntax because starting v3, node-fetch is an ESM only module. If you are using Node.js version earlier than 12.20.0 or need to use CommonJS syntax (require syntax: const fetch = require(«node-fetch») ), then you can install node-fetch version 2 using npm i [email protected] .

Update the package.json with type as module . This is required to tell Node.js to use ESM Module syntax, since, by default, Node.js uses CommonJS syntax.

Now if you run the code, it should work properly.

If you have liked article, stay in touch with me by following me on twitter.

Источник

Brian Cline

Fetch is a relatively new addition to the browsers which allows us to avoid adding libraries to our browser-based applications. (You can learn more from Using the New JavaScript Fetch HTTP API blog article).

As you may have noticed, fetch doesn’t work in Node.js. If you attempt to use it you get an error like the below one

The fetch API isn’t implemented in Node, so you need to use a package that implements it and then use that.

There are quite a few different modules available, I’m tending to use node-fetch as it more resembles the native fetch is ES6. The node-fetch package is pretty lightweight and is quite a bit smaller than axios.

So, how do we use node-fetch then?

We need to install the node package using npm or yarn. I prefer to use npm as it works well enough for me (and it comes with Node on Windows!) To install it, I simply need to type the following command into the terminal

npm i node-fetch

It will run for a moment or two and then show results like the below image

Using the library inside of our code is actually pretty easy. For the most part, I just require it at the top of my modules and I’m good to go.

Wrapping it Up

For now, Node.js doesn’t implement fetch. Hopefully, some day it is added in and we no longer need to add another dependency.

For now, I recommend using a dependency like node-fetch or looking at request or axios.

Источник

Using the Fetch API

The Fetch API provides a JavaScript interface for accessing and manipulating parts of the protocol, such as requests and responses. It also provides a global fetch() method that provides an easy, logical way to fetch resources asynchronously across the network.

This kind of functionality was previously achieved using XMLHttpRequest . Fetch provides a better alternative that can be easily used by other technologies such as Service Workers . Fetch also provides a single logical place to define other HTTP-related concepts such as CORS and extensions to HTTP.

The fetch specification differs from jQuery.ajax() in the following significant ways:

  • The Promise returned from fetch() won’t reject on HTTP error status even if the response is an HTTP 404 or 500. Instead, as soon as the server responds with headers, the Promise will resolve normally (with the ok property of the response set to false if the response isn’t in the range 200–299), and it will only reject on network failure or if anything prevented the request from completing.
  • Unless fetch() is called with the credentials option set to include , fetch() :
    • won’t send cookies in cross-origin requests
    • won’t set any cookies sent back in cross-origin responses
    • As of August 2018, the default credentials policy changed to same-origin. Firefox was also modified in version 61.0b13)

A basic fetch request is really simple to set up. Have a look at the following code:

Here we are fetching a JSON file across the network and printing it to the console. The simplest use of fetch() takes one argument — the path to the resource you want to fetch — and does not directly return the JSON response body but instead returns a promise that resolves with a Response object.

The Response object, in turn, does not directly contain the actual JSON response body but is instead a representation of the entire HTTP response. So, to extract the JSON body content from the Response object, we use the json() method, which returns a second promise that resolves with the result of parsing the response body text as JSON.

Note: See the Body section for similar methods to extract other types of body content.

Fetch requests are controlled by the connect-src directive of Content Security Policy rather than the directive of the resources it’s retrieving.

Supplying request options

The fetch() method can optionally accept a second parameter, an init object that allows you to control a number of different settings:

See fetch() for the full options available, and more details.

Note that mode: «no-cors» only allows a limited set of headers in the request:

  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type with a value of application/x-www-form-urlencoded , multipart/form-data , or text/plain

Sending a request with credentials included

To cause browsers to send a request with credentials included on both same-origin and cross-origin calls, add credentials: ‘include’ to the init object you pass to the fetch() method.

Note: Access-Control-Allow-Origin is prohibited from using a wildcard for requests with credentials: ‘include’ . In such cases, the exact origin must be provided; even if you are using a CORS unblocker extension, the requests will still fail.

Note: Browsers should not send credentials in preflight requests irrespective of this setting. For more information see: CORS > Requests with credentials.

If you only want to send credentials if the request URL is on the same origin as the calling script, add credentials: ‘same-origin’ .

To instead ensure browsers don’t include credentials in the request, use credentials: ‘omit’ .

Uploading JSON data

Use fetch() to POST JSON-encoded data.

Uploading a file

Files can be uploaded using an HTML input element, FormData() and fetch() .

Uploading multiple files

Files can be uploaded using an HTML input element, FormData() and fetch() .

Processing a text file line by line

The chunks that are read from a response are not broken neatly at line boundaries and are Uint8Arrays, not strings. If you want to fetch a text file and process it line by line, it is up to you to handle these complications. The following example shows one way to do this by creating a line iterator (for simplicity, it assumes the text is UTF-8, and doesn’t handle fetch errors).

Checking that the fetch was successful

A fetch() promise will reject with a TypeError when a network error is encountered or CORS is misconfigured on the server-side, although this usually means permission issues or similar — a 404 does not constitute a network error, for example. An accurate check for a successful fetch() would include checking that the promise resolved, then checking that the Response.ok property has a value of true. The code would look something like this:

Supplying your own request object

Instead of passing a path to the resource you want to request into the fetch() call, you can create a request object using the Request() constructor, and pass that in as a fetch() method argument:

Request() accepts exactly the same parameters as the fetch() method. You can even pass in an existing request object to create a copy of it:

This is pretty useful, as request and response bodies can only be used once. Making a copy like this allows you to effectively use the request/response again while varying the init options if desired. The copy must be made before the body is read.

Note: There is also a clone() method that creates a copy. Both methods of creating a copy will fail if the body of the original request or response has already been read, but reading the body of a cloned response or request will not cause it to be marked as read in the original.

The Headers interface allows you to create your own headers object via the Headers() constructor. A headers object is a simple multi-map of names to values:

The same can be achieved by passing an array of arrays or an object literal to the constructor:

The contents can be queried and retrieved:

Some of these operations are only useful in ServiceWorkers , but they provide a much nicer API for manipulating headers.

All of the Headers methods throw a TypeError if a header name is used that is not a valid HTTP Header name. The mutation operations will throw a TypeError if there is an immutable guard (see below). Otherwise, they fail silently. For example:

A good use case for headers is checking whether the content type is correct before you process it further. For example:

Guard

Since headers can be sent in requests and received in responses, and have various limitations about what information can and should be mutable, headers’ objects have a guard property. This is not exposed to the Web, but it affects which mutation operations are allowed on the headers object.

Possible guard values are:

  • none : default.
  • request : guard for a headers object obtained from a request ( Request.headers ).
  • request-no-cors : guard for a headers object obtained from a request created with Request.mode no-cors .
  • response : guard for a headers object obtained from a response ( Response.headers ).
  • immutable : guard that renders a headers object read-only; mostly used for ServiceWorkers.

Note: You may not append or set the Content-Length header on a guarded headers object for a response . Similarly, inserting Set-Cookie into a response header is not allowed: ServiceWorkers are not allowed to set cookies via synthesized responses.

Response objects

As you have seen above, Response instances are returned when fetch() promises are resolved.

The most common response properties you’ll use are:

  • Response.status — An integer (default value 200) containing the response status code.
  • Response.statusText — A string (default value «»), which corresponds to the HTTP status code message. Note that HTTP/2 does not support status messages.
  • Response.ok — seen in use above, this is a shorthand for checking that status is in the range 200-299 inclusive. This returns a boolean value.

They can also be created programmatically via JavaScript, but this is only really useful in ServiceWorkers , when you are providing a custom response to a received request using a respondWith() method:

The Response() constructor takes two optional arguments — a body for the response, and an init object (similar to the one that Request() accepts.)

Note: The static method error() returns an error response. Similarly, redirect() returns a response resulting in a redirect to a specified URL. These are also only relevant to Service Workers.

Both requests and responses may contain body data. A body is an instance of any of the following types:

The Request and Response interfaces share the following methods to extract a body. These all return a promise that is eventually resolved with the actual content.

This makes usage of non-textual data much easier than it was with XHR.

Request bodies can be set by passing body parameters:

Both request and response (and by extension the fetch() function), will try to intelligently determine the content type. A request will also automatically set a Content-Type header if none is set in the dictionary.

Feature detection

Fetch API support can be detected by checking for the existence of Headers , Request , Response or fetch() on the Window or Worker scope. For example:

Источник

The Fetch API provides a JavaScript interface for accessing and manipulating parts of the protocol, such as requests and responses. It also provides a global fetch() method that provides an easy, logical way to fetch resources asynchronously across the network.

This kind of functionality was previously achieved using XMLHttpRequest. Fetch provides a better alternative that can be easily used by other technologies such as Service Workers. Fetch also provides a single logical place to define other HTTP-related concepts such as CORS and extensions to HTTP.

The fetch specification differs from jQuery.ajax() in the following significant ways:

  • The Promise returned from fetch() won’t reject on HTTP error status even if the response is an HTTP 404 or 500. Instead, as soon as the server responds with headers, the Promise will resolve normally (with the ok property of the response set to false if the response isn’t in the range 200–299), and it will only reject on network failure or if anything prevented the request from completing.
  • Unless fetch() is called with the credentials option set to include, fetch():
    • won’t send cookies in cross-origin requests
    • won’t set any cookies sent back in cross-origin responses
    • As of August 2018, the default credentials policy changed to same-origin. Firefox was also modified in version 61.0b13)

A basic fetch request is really simple to set up. Have a look at the following code:

fetch('http://example.com/movies.json')
  .then((response) => response.json())
  .then((data) => console.log(data));

Here we are fetching a JSON file across the network and printing it to the console. The simplest use of fetch() takes one argument — the path to the resource you want to fetch — and does not directly return the JSON response body but instead returns a promise that resolves with a Response object.

The Response object, in turn, does not directly contain the actual JSON response body but is instead a representation of the entire HTTP response. So, to extract the JSON body content from the Response object, we use the json() method, which returns a second promise that resolves with the result of parsing the response body text as JSON.

Note: See the Body section for similar methods to extract other types of body content.

Fetch requests are controlled by the connect-src directive of Content Security Policy rather than the directive of the resources it’s retrieving.

Supplying request options

The fetch() method can optionally accept a second parameter, an init object that allows you to control a number of different settings:

See fetch() for the full options available, and more details.

// Example POST method implementation:
async function postData(url = '', data = {}) {
  // Default options are marked with *
  const response = await fetch(url, {
    method: 'POST', // *GET, POST, PUT, DELETE, etc.
    mode: 'cors', // no-cors, *cors, same-origin
    cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
    credentials: 'same-origin', // include, *same-origin, omit
    headers: {
      'Content-Type': 'application/json'
      // 'Content-Type': 'application/x-www-form-urlencoded',
    },
    redirect: 'follow', // manual, *follow, error
    referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
    body: JSON.stringify(data) // body data type must match "Content-Type" header
  });
  return response.json(); // parses JSON response into native JavaScript objects
}

postData('https://example.com/answer', { answer: 42 })
  .then((data) => {
    console.log(data); // JSON data parsed by `data.json()` call
  });

Note that mode: "no-cors" only allows a limited set of headers in the request:

  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type with a value of application/x-www-form-urlencoded, multipart/form-data, or text/plain

Sending a request with credentials included

To cause browsers to send a request with credentials included on both same-origin and cross-origin calls, add credentials: 'include' to the init object you pass to the fetch() method.

fetch('https://example.com', {
  credentials: 'include'
});

Note: Access-Control-Allow-Origin is prohibited from using a wildcard for requests with credentials: 'include'. In such cases, the exact origin must be provided; even if you are using a CORS unblocker extension, the requests will still fail.

Note: Browsers should not send credentials in preflight requests irrespective of this setting. For more information see: CORS > Requests with credentials.

If you only want to send credentials if the request URL is on the same origin as the calling script, add credentials: 'same-origin'.

// The calling script is on the origin 'https://example.com'

fetch('https://example.com', {
  credentials: 'same-origin'
});

To instead ensure browsers don’t include credentials in the request, use credentials: 'omit'.

fetch('https://example.com', {
  credentials: 'omit'
})

Uploading JSON data

Use fetch() to POST JSON-encoded data.

const data = { username: 'example' };

fetch('https://example.com/profile', {
  method: 'POST', // or 'PUT'
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(data),
})
  .then((response) => response.json())
  .then((data) => {
    console.log('Success:', data);
  })
  .catch((error) => {
    console.error('Error:', error);
  });

Uploading a file

Files can be uploaded using an HTML <input type="file" /> input element, FormData() and fetch().

const formData = new FormData();
const fileField = document.querySelector('input[type="file"]');

formData.append('username', 'abc123');
formData.append('avatar', fileField.files[0]);

fetch('https://example.com/profile/avatar', {
  method: 'PUT',
  body: formData
})
  .then((response) => response.json())
  .then((result) => {
    console.log('Success:', result);
  })
  .catch((error) => {
    console.error('Error:', error);
  });

Uploading multiple files

Files can be uploaded using an HTML <input type="file" multiple /> input element, FormData() and fetch().

const formData = new FormData();
const photos = document.querySelector('input[type="file"][multiple]');

formData.append('title', 'My Vegas Vacation');
let i = 0;
for (const photo of photos.files) {
  formData.append(`photos_${i}`, photo);
  i++;
}

fetch('https://example.com/posts', {
  method: 'POST',
  body: formData,
})
  .then((response) => response.json())
  .then((result) => {
    console.log('Success:', result);
  })
  .catch((error) => {
    console.error('Error:', error);
  });

Processing a text file line by line

The chunks that are read from a response are not broken neatly at line boundaries and are Uint8Arrays, not strings. If you want to fetch a text file and process it line by line, it is up to you to handle these complications. The following example shows one way to do this by creating a line iterator (for simplicity, it assumes the text is UTF-8, and doesn’t handle fetch errors).

async function* makeTextFileLineIterator(fileURL) {
  const utf8Decoder = new TextDecoder('utf-8');
  const response = await fetch(fileURL);
  const reader = response.body.getReader();
  let { value: chunk, done: readerDone } = await reader.read();
  chunk = chunk ? utf8Decoder.decode(chunk) : '';

  const re = /n|r|rn/gm;
  let startIndex = 0;
  let result;

  while (true) {
    let result = re.exec(chunk);
    if (!result) {
      if (readerDone) break;
      let remainder = chunk.substr(startIndex);
      ({ value: chunk, done: readerDone } = await reader.read());
      chunk = remainder + (chunk ? utf8Decoder.decode(chunk) : '');
      startIndex = re.lastIndex = 0;
      continue;
    }
    yield chunk.substring(startIndex, result.index);
    startIndex = re.lastIndex;
  }

  if (startIndex < chunk.length) {
    // Last line didn't end in a newline char
    yield chunk.substr(startIndex);
  }
}

async function run() {
  for await (const line of makeTextFileLineIterator(urlOfFile)) {
    processLine(line);
  }
}

run();

Checking that the fetch was successful

A fetch() promise will reject with a TypeError when a network error is encountered or CORS is misconfigured on the server-side, although this usually means permission issues or similar — a 404 does not constitute a network error, for example. An accurate check for a successful fetch() would include checking that the promise resolved, then checking that the Response.ok property has a value of true. The code would look something like this:

fetch('flowers.jpg')
  .then((response) => {
    if (!response.ok) {
      throw new Error('Network response was not OK');
    }
    return response.blob();
  })
  .then((myBlob) => {
    myImage.src = URL.createObjectURL(myBlob);
  })
  .catch((error) => {
    console.error('There has been a problem with your fetch operation:', error);
  });

Supplying your own request object

Instead of passing a path to the resource you want to request into the fetch() call, you can create a request object using the Request() constructor, and pass that in as a fetch() method argument:

const myHeaders = new Headers();

const myRequest = new Request('flowers.jpg', {
  method: 'GET',
  headers: myHeaders,
  mode: 'cors',
  cache: 'default',
});

fetch(myRequest)
  .then((response) => response.blob())
  .then((myBlob) => {
    myImage.src = URL.createObjectURL(myBlob);
  });

Request() accepts exactly the same parameters as the fetch() method. You can even pass in an existing request object to create a copy of it:

const anotherRequest = new Request(myRequest, myInit);

This is pretty useful, as request and response bodies can only be used once.
Making a copy like this allows you to effectively use the request/response again while varying the init options if desired.
The copy must be made before the body is read.

Note: There is also a clone() method that creates a copy. Both methods of creating a copy will fail if the body of the original request or response has already been read, but reading the body of a cloned response or request will not cause it to be marked as read in the original.

The Headers interface allows you to create your own headers object via the Headers() constructor. A headers object is a simple multi-map of names to values:

const content = 'Hello World';
const myHeaders = new Headers();
myHeaders.append('Content-Type', 'text/plain');
myHeaders.append('Content-Length', content.length.toString());
myHeaders.append('X-Custom-Header', 'ProcessThisImmediately');

The same can be achieved by passing an array of arrays or an object literal to the constructor:

const myHeaders = new Headers({
  'Content-Type': 'text/plain',
  'Content-Length': content.length.toString(),
  'X-Custom-Header': 'ProcessThisImmediately'
});

The contents can be queried and retrieved:

console.log(myHeaders.has('Content-Type')); // true
console.log(myHeaders.has('Set-Cookie')); // false
myHeaders.set('Content-Type', 'text/html');
myHeaders.append('X-Custom-Header', 'AnotherValue');

console.log(myHeaders.get('Content-Length')); // 11
console.log(myHeaders.get('X-Custom-Header')); // ['ProcessThisImmediately', 'AnotherValue']

myHeaders.delete('X-Custom-Header');
console.log(myHeaders.get('X-Custom-Header')); // null

Some of these operations are only useful in ServiceWorkers, but they provide a much nicer API for manipulating headers.

All of the Headers methods throw a TypeError if a header name is used that is not a valid HTTP Header name. The mutation operations will throw a TypeError if there is an immutable guard (see below). Otherwise, they fail silently. For example:

const myResponse = Response.error();
try {
  myResponse.headers.set('Origin', 'http://mybank.com');
} catch (e) {
  console.log('Cannot pretend to be a bank!');
}

A good use case for headers is checking whether the content type is correct before you process it further. For example:

fetch(myRequest)
  .then((response) => {
     const contentType = response.headers.get('content-type');
     if (!contentType || !contentType.includes('application/json')) {
       throw new TypeError("Oops, we haven't got JSON!");
     }
     return response.json();
  })
  .then((data) => {
      /* process your data further */
  })
  .catch((error) => console.error(error));

Guard

Since headers can be sent in requests and received in responses, and have various limitations about what information can and should be mutable, headers’ objects have a guard property. This is not exposed to the Web, but it affects which mutation operations are allowed on the headers object.

Possible guard values are:

  • none: default.
  • request: guard for a headers object obtained from a request (Request.headers).
  • request-no-cors: guard for a headers object obtained from a request created with Request.mode no-cors.
  • response: guard for a headers object obtained from a response (Response.headers).
  • immutable: guard that renders a headers object read-only; mostly used for ServiceWorkers.

Note: You may not append or set the Content-Length header on a guarded headers object for a response. Similarly, inserting Set-Cookie into a response header is not allowed: ServiceWorkers are not allowed to set cookies via synthesized responses.

Response objects

As you have seen above, Response instances are returned when fetch() promises are resolved.

The most common response properties you’ll use are:

  • Response.status — An integer (default value 200) containing the response status code.
  • Response.statusText — A string (default value «»), which corresponds to the HTTP status code message. Note that HTTP/2 does not support status messages.
  • Response.ok — seen in use above, this is a shorthand for checking that status is in the range 200-299 inclusive. This returns a boolean value.

They can also be created programmatically via JavaScript, but this is only really useful in ServiceWorkers, when you are providing a custom response to a received request using a respondWith() method:

const myBody = new Blob();

addEventListener('fetch', (event) => {
  // ServiceWorker intercepting a fetch
  event.respondWith(
    new Response(myBody, {
      headers: { 'Content-Type': 'text/plain' }
    })
  );
});

The Response() constructor takes two optional arguments — a body for the response, and an init object (similar to the one that Request() accepts.)

Note: The static method error() returns an error response. Similarly, redirect() returns a response resulting in a redirect to a specified URL. These are also only relevant to Service Workers.

Body

Both requests and responses may contain body data. A body is an instance of any of the following types:

  • ArrayBuffer
  • TypedArray (Uint8Array and friends)
  • DataView
  • Blob
  • File
  • String, or a string literal
  • URLSearchParams
  • FormData

The Request and Response interfaces share the following methods to extract a body. These all return a promise that is eventually resolved with the actual content.

  • Request.arrayBuffer() / Response.arrayBuffer()
  • Request.blob() / Response.blob()
  • Request.formData() / Response.formData()
  • Request.json() / Response.json()
  • Request.text() / Response.text()

This makes usage of non-textual data much easier than it was with XHR.

Request bodies can be set by passing body parameters:

const form = new FormData(document.getElementById('login-form'));
fetch('/login', {
  method: 'POST',
  body: form
});

Both request and response (and by extension the fetch() function), will try to intelligently determine the content type. A request will also automatically set a Content-Type header if none is set in the dictionary.

Feature detection

Fetch API support can be detected by checking for the existence of Headers, Request, Response or fetch() on the Window or Worker scope. For example:

if (window.fetch) {
  // run my fetch request here
} else {
  // do something with XMLHttpRequest?
}

Specifications

Specification
Fetch Standard
# fetch-method

Browser compatibility

BCD tables only load in the browser

See also

Время прочтения
5 мин

Просмотры 268K

Прощай, XMLHttpRequest!

fetch() позволяет вам делать запросы, схожие с XMLHttpRequest (XHR). Основное отличие заключается в том, что Fetch API использует Promises (Обещания), которые позволяют использовать более простое и чистое API, избегать катастрофического количества callback’ов и необходимости помнить API для XMLHttpRequest.

Fetch API стал доступен пользователям вместе с Service Worker’ами в global скоупе в Chrome 40, однако уже в версии 42 он станет доступен в скоупе window. Разумеется, для всех остальных браузеров, которые пока ещё не поддерживают fetch существует полифил от GitHub, который доступен уже сегодня.

Простой Fetch запрос

Давайте начнём со сравнения простого примера, реализованного с XMLHttpRequest и fetch. Всё, что мы будем делать в этом примере — сделаем запрос на URL, получим ответ и распарсим его как JSON.

XMLHttpRequest

Пример с XMLHttpRequest потребует от нас установить два обработчика событий на success и error, а так же вызвать два метода: open() и send(). Пример из MDN документации:

function reqListener() {  
  var data = JSON.parse(this.responseText);  
  console.log(data);  
}

function reqError(err) {  
  console.log('Fetch Error :-S', err);  
}

var oReq = new XMLHttpRequest();  
oReq.onload = reqListener;  
oReq.onerror = reqError;  
oReq.open('get', './api/some.json', true);  
oReq.send();

Fetch

Наш fetch запрос будет выглядеть так:

fetch('./api/some.json')  
  .then(  
    function(response) {  
      if (response.status !== 200) {  
        console.log('Looks like there was a problem. Status Code: ' +  
          response.status);  
        return;  
      }

      // Examine the text in the response  
      response.json().then(function(data) {  
        console.log(data);  
      });  
    }  
  )  
  .catch(function(err) {  
    console.log('Fetch Error :-S', err);  
  });

Первым делом мы проверяем статус ответа и проверяем, успешно ли выполнился запрос (ожидаем 200 статус). Если всё хорошо, то парсим ответ как JSON.

Ответом fetch() является Stream-объект. Это означает, что в результате вызова метода json() мы получим Promise, т.к. чтение из подобного объекта является асинхронным.

Метаданные ответа

В предыдущем примере мы изучили, как можно проверить статус объекта ответа и конвентировать сам ответ в JSON. Остальные метаданные, к которым вы возможно получить доступ (например, заголовки), приведены ниже:

fetch('users.json').then(function(response) {  
    console.log(response.headers.get('Content-Type'));  
    console.log(response.headers.get('Date'));

    console.log(response.status);  
    console.log(response.statusText);  
    console.log(response.type);  
    console.log(response.url);  
});

Типы ответа

Когда мы делаем fetch-запрос, ответу будет дан тип «basic», «cors» или «opaque». Эти «типы» указывают на то, с какого ресурса пришли данные и могут быть использованы для того, чтобы определить процесс обработки данных.

Когда запрос сделан на ресурс, находящимся на том же origin (имеется ввиду, что запрос выполняется в рамках одного сайта. прим. пер.), ответ будет содержать тип «базовый» и для такого запроса не будет никаких ограничений.

Если запрос сделан с одного origin’а на другой (кроссдоменный запрос), который, в свою очередь, вернул CORS заголовки, тогда типом будет являться «cors». Объекты с типами «cors» и «basic» почти идентичны, однако «cors» несколько ограничивает метаданные, к которым может быть получен доступ до «Cache-Control», «Content-Language», «Content-Type», «Expires», «Last-Modified», и «Pragma».

Что касается «opaque» — то он приходит в случаях, когда выполняется CORS запрос, но удаленный ресурс не возвращает CORS заголовки. Данный тип запроса не предоставляет доступ данным или заголовку статуса, поэтому мы не имеем возможности судить о результате выполнения запроса. В рамках текущей имплементации fetch() не представляется возможности выполнять CORS запросы из скоупа window, и вот здесь написано почему. Эта функциональность должна быть добавлена, как только Cache API станет доступным из объекта window.

Вы можете определить ожидаемый режим запроса, тем самым фильтруя результаты запросов с неподходящим типом. Режим запроса может быть установлен на следующий:

— “same-origin” успешно выполняется только для запросов на тот же самый origin, все остальные запросы будут отклонены.
— “cors” работает так же, как «same-origin» + добавляет возможность создавать запросы к сторонним сайтам, если они возвращают соответствующие CORS заголовки.
— “cors-with-forced-preflight” работает так же, как «cors», но перед запросом всегда отсылает тестовый запрос на проверку.
— “no-cors” используется, когда необходимо выполнить запрос к origin, который не отсылает CORS заголовки и результатом выполнения является объект с типом «opaque». Как говорилось выше, в данный момент это невозможно в скоупе window.

Чтобы определить режим запроса, добавьте объект опций вторым параметром к запросу и установите «mode» в этом объекте:

fetch('http://some-site.com/cors-enabled/some.json', {mode: 'cors'})  
  .then(function(response) {  
    return response.text();  
  })  
  .then(function(text) {  
    console.log('Request successful', text);  
  })  
  .catch(function(error) {  
    log('Request failed', error)  
  });

Цепочки Promises

Одной из прекрасных особенностей Promise’ов является возможность группировать их в цепочки. Если говорить о них в скоупе fetch(), то они позволяют нам «шарить» логику между запросами.

Если вы работаете с JSON API, вам потребуется проверить статус и распарсить JSON для каждого ответа. Вы можете упростить свой код, определив парсинг статуса и JSON как раздельные функции, которые вернут Promise’ы. Вам останется подумать только об обработке самих данных и, разумеется, исключений.

function status(response) {  
  if (response.status >= 200 && response.status < 300) {  
    return Promise.resolve(response)  
  } else {  
    return Promise.reject(new Error(response.statusText))  
  }  
}

function json(response) {  
  return response.json()  
}

fetch('users.json')  
  .then(status)  
  .then(json)  
  .then(function(data) {  
    console.log('Request succeeded with JSON response', data);  
  }).catch(function(error) {  
    console.log('Request failed', error);  
  });

Мы определяем функцию, которая проверяет response.status и возвращает результат: Promise.resolve() или Promise.reject(). Это первый вызванный метод в нашей цепочке, и если он успешно завершается(Promise.resolve()), то вызывается следующий за ним метод — fetch(), который, в свою очередь, опять возвращает Promise от response.json(). После этого вызова, в случае удачного выполнения, у нас будет готовый JSON объект. Если парсинг провалится, Promise будет отменен и сработает условие возникновения исключения.

Но самое лучшее здесь — это возможность переиспользовать такой код для всех fetch-запросов в приложении. Такой код проще поддерживать, читать и тестировать.

POST запрос

Уже давно никого не удивишь необходимостью использовать POST метод с передачей параметров в «теле» запроса для работы с API.
Чтобы осуществить такой запрос, мы должны указать соответствующие параметры в объекте настроек fetch():

fetch(url, {  
    method: 'post',  
    headers: {  
      "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"  
    },  
    body: 'foo=bar&lorem=ipsum'  
  })
  .then(json)  
  .then(function (data) {  
    console.log('Request succeeded with JSON response', data);  
  })  
  .catch(function (error) {  
    console.log('Request failed', error);  
  });

Посылаем учётные данные через Fetch-запрос

Если вы хотите отправить запрос с каким-либо учётными данными (например, с cookie), вам следует установить `credentials` в опциях запроса на «include»:

fetch(url, {  
  credentials: 'include'  
})

FAQ

Могу ли я отменить fetch() запрос?
В настоящий момент это невозможно, но это активно обсуждается на GitHub

Существует ли полифил?
Да

Почему «no-cors» реализован для service workers, но не для window?
Это было сделано из соображений безопасности. Подробнее можно ознакомиться здесь.

This is a quick example of how to handle both network errors and HTTP errors (4xx or 5xx) for Fetch API (fetch()) requests in a single catch() block.

GET request with error handling

This sends an HTTP GET request to an invalid URL on the Reqres api which is a fake online REST api used for testing, then writes the error message to the parent of the #get-request-error-handling .total element and logs the error to the console.

Error Handling

The fetch() function will automatically throw an error for network errors but not for HTTP errors such as 4xx or 5xx responses. For HTTP errors we can check the response.ok property to see if the request failed and reject the promise ourselves by calling return Promise.reject(error);. This approach means that both types of failed requests — network errors and http errors — can be handled by a single catch() block instead of needing two separate pieces of error handling code.

JSON Check

The fetch .then() callback is passed the HTTP response object when the request is completed, the function checks if the response type is JSON before parsing the response body with the response.json() method, because calling response.json() will cause an error if the response doesn’t contain JSON data.

// Fetch GET request with error handling
const element = document.querySelector('#get-request .result');
fetch('https://reqres.in/invalid-url')
    .then(async response => {
        const isJson = response.headers.get('content-type')?.includes('application/json');
        const data = isJson ? await response.json() : null;

        // check for error response
        if (!response.ok) {
            // get error message from body or default to response status
            const error = (data && data.message) || response.status;
            return Promise.reject(error);
        }

        element.innerHTML = JSON.stringify(data, null, 4);
    })
    .catch(error => {
        element.parentElement.innerHTML = `Error: ${error}`;
        console.error('There was an error!', error);
    });

Run the example Fetch GET request on StackBlitz at https://stackblitz.com/edit/fetch-error-handling-http-and-network-errors

Subscribe or Follow Me For Updates

Subscribe to my YouTube channel or follow me on Twitter, Facebook or GitHub to be notified when I post new content.

  • Subscribe on YouTube at https://www.youtube.com/JasonWatmore
  • Follow me on Twitter at https://twitter.com/jason_watmore
  • Follow me on Facebook at https://www.facebook.com/JasonWatmoreBlog
  • Follow me on GitHub at https://github.com/cornflourblue
  • Feed formats available:
    RSS,
    Atom,
    JSON

Other than coding…

I’m currently attempting to travel around Australia by motorcycle with my wife Tina on a pair of Royal Enfield Himalayans. You can follow our adventures on YouTube, Instagram and Facebook.

  • Subscribe on YouTube at https://www.youtube.com/TinaAndJason
  • Follow us on Instagram at https://www.instagram.com/tinaandjason
  • Follow us on Facebook at https://www.facebook.com/TinaAndJasonVlog
  • Visit our website at https://tinaandjason.com.au

Need Some Fetch Help?

Search fiverr to find help quickly from experienced Fetch developers.



Понравилась статья? Поделить с друзьями:
  • Node js error stack trace
  • Node js error socket hang up
  • Node js error object
  • Node js error message
  • Node js error cannot find module express