Сетевые вызовы в Node.js с помощью Got

Раздел: Node.js -> сетевые библиотеки

Библиотека Got для HTTP запросов в Node.js

Как выполнить базовый GET запрос?

Для начала устанавливается пакет got через npm:

npm install got

Got js (библиотека got для http запросов)

После установки импортируется функция got и вызывается с URL. Результат содержит данные и статус ответа.

import got from 'got';

async function fetchData() {
  try {
    const response = await got('https://jsonplaceholder.typicode.com/posts/1');
    console.log(response.statusCode); // 200
    console.log(response.body);       // JSON строка
  } catch (error) {
    console.error(error.message);
  }
}

fetchData();

По умолчанию Got автоматически парсит JSON, если в ответе указан заголовок Content-Type: application/json. В противном случае response.body остаётся строкой.

Типичная ошибка: При запросе к несуществующему адресу Got выбрасывает ошибку HTTPError. Если не использовать try/catch, приложение аварийно завершится. Всегда обрабатывайте ошибки через try/catch или .catch().

Как отправить POST запрос с JSON телом?

Для отправки данных методом POST используется опция json или body. Got автоматически устанавливает заголовок Content-Type: application/json при указании json.

import got from 'got';

async function createPost() {
  try {
    const response = await got.post('https://jsonplaceholder.typicode.com/posts', {
      json: {
        title: 'Новая запись',
        body: 'Текст записи',
        userId: 1
      }
    });
    console.log('Статус:', response.statusCode); // 201
    console.log('Создан:', response.body);
  } catch (error) {
    console.error('Ошибка POST:', error.message);
  }
}

createPost();

Если сервер ожидает другой формат (например, form-data), используйте опцию form или укажите body вручную с соответствующим Content-Type.

Возможная проблема: Неправильный формат данных (например, передача строки вместо объекта) может привести к тому, что сервер не распознает запрос. Всегда проверяйте документацию API.

Как обрабатывать HTTP ошибки (4xx/5xx) и сетевые ошибки отдельно?

Got разделяет ошибки по типам. Для HTTP статусов генерируется HTTPError, для превышения таймаута — TimeoutError, для сетевых — RequestError. Каждую можно проверить через instanceof.

import got, { HTTPError, TimeoutError } from 'got';

async function safeRequest(url) {
  try {
    const response = await got(url);
    return response.body;
  } catch (error) {
    if (error instanceof HTTPError) {
      console.error('HTTP ошибка:', error.response.statusCode, error.response.body);
    } else if (error instanceof TimeoutError) {
      console.error('Таймаут запроса');
    } else {
      console.error('Сетевая ошибка:', error.message);
    }
    throw error;
  }
}

safeRequest('https://example.com/api');

Распространённая ошибка: Новички перехватывают ошибку общей конструкцией, не различая тип, и теряют детали. Рекомендуется всегда использовать проверку instanceof для точного определения.

Как скачать файл через потоки, не загружая его целиком в память?

Got предоставляет метод .stream(), который возвращает ReadableStream. Его можно направить в файл через fs.createWriteStream. Это эффективно для больших файлов.

import got from 'got';
import fs from 'fs';
import path from 'path';

const url = 'https://example.com/bigfile.zip';
const destination = path.resolve('download.zip');

const downloadStream = got.stream(url);
const fileStream = fs.createWriteStream(destination);

downloadStream.on('error', (err) => {
  console.error('Ошибка загрузки:', err.message);
});

fileStream.on('error', (err) => {
  console.error('Ошибка записи:', err.message);
});

fileStream.on('finish', () => {
  console.log('Файл успешно загружен:', destination);
});

downloadStream.pipe(fileStream);

Можно также использовать события .on('downloadProgress') для отслеживания прогресса.

Проблема: Если не обработать ошибки на обоих потоках, файл может остаться частично записанным. Всегда добавляйте обработчики error.

Как добавить перехватчики (hooks) для логирования или модификации запроса?

Перехватчики позволяют вмешиваться в жизненный цикл запроса. С помощью got.extend() создается экземпляр с хуками beforeRequest, afterResponse и другими.

import got from 'got';

const client = got.extend({
  hooks: {
    beforeRequest: [
      (options) => {
        console.log('Запрос к:', options.url.href);
      }
    ],
    afterResponse: [
      (response) => {
        console.log('Статус ответа:', response.statusCode);
        return response;
      }
    ]
  }
});

async function testHook() {
  const response = await client.get('https://jsonplaceholder.typicode.com/todos/1');
  console.log('Результат:', response.body);
}

testHook();

Хуки можно комбинировать: добавлять авторизацию, изменять тело запроса, ретраить запросы при ошибках.

Ошибка: В хуке beforeRequest изменение options не должно быть асинхронным без проброса ошибок. Если хук возвращает Promise, убедитесь, что он корректно обрабатывается.

Как создать кастомный клиент с общими настройками (базовый URL, заголовки)?

got.extend() создаёт новый экземпляр с переопределёнными параметрами. Это удобно для работы с одним API.

import got from 'got';

const apiClient = got.extend({
  prefixUrl: 'https://api.example.com/v1',
  headers: {
    'Authorization': 'Bearer токен_доступа',
    'Accept': 'application/json'
  },
  timeout: {
    request: 5000
  },
  retry: {
    limit: 2
  }
});

async function getUser(id) {
  const response = await apiClient.get(`users/${id}`);
  return response.body;
}

getUser(42).then(console.log).catch(console.error);

Такой подход сокращает дублирование кода и централизует настройки.

Типичная ошибка: При передаче полного URL в метод apiClient.get('https://other.com/path') префикс будет проигнорирован. Используйте только относительные пути, если задан prefixUrl.

Расширенные примеры использования Got

Потоковая загрузка с прогрессом и сохранением в файл

Пример демонстрирует загрузку большого файла с индикацией прогресса и проверкой ошибок на каждом этапе.

Пример
import got from 'got';
import fs from 'fs';
import path from 'path';

const url = 'https://speed.hetzner.de/100MB.bin';
const destination = path.resolve('testfile.bin');

const downloadStream = got.stream(url);
const writeStream = fs.createWriteStream(destination);

let transferred = 0;
downloadStream.on('downloadProgress', (progress) => {
  const percent = Math.round(progress.percent * 100);
  if (percent !== transferred) {
    transferred = percent;
    process.stdout.write(`\rЗагружено: ${percent}%`);
  }
});

downloadStream.on('error', (err) => {
  console.error('\nОшибка скачивания:', err.message);
  fs.unlinkSync(destination); // удаляем недогруженный файл
});

writeStream.on('error', (err) => {
  console.error('\nОшибка записи:', err.message);
  downloadStream.destroy();
});

writeStream.on('finish', () => {
  console.log('\nФайл сохранён:', destination);
});

downloadStream.pipe(writeStream);
Загружено: 100%
Файл сохранён: /путь/testfile.bin

Параллельные запросы с ограничением конкурентности

Для выполнения нескольких запросов одновременно используется Promise.all. Однако для управления нагрузкой можно применить пакет p-limit.

Пример
import got from 'got';
import pLimit from 'p-limit';

const limit = pLimit(3); // максимально 3 одновременных запроса

const urls = [
  'https://jsonplaceholder.typicode.com/posts/1',
  'https://jsonplaceholder.typicode.com/posts/2',
  'https://jsonplaceholder.typicode.com/posts/3',
  'https://jsonplaceholder.typicode.com/posts/4',
  'https://jsonplaceholder.typicode.com/posts/5'
];

const requests = urls.map(url => limit(() => got(url).json()));

const results = await Promise.all(requests);
results.forEach((data, index) => {
  console.log(`Запрос ${index+1}:`, data.title);
});
Запрос 1: sunt aut facere repellat provident occaecati excepturi optio reprehenderit
Запрос 2: qui est esse
Запрос 3: ea molestias quasi exercitationem repellat
Запрос 4: eum et est occaecati
Запрос 5: nesciunt quas odio

Автоматическая пагинация через перехватчик

Можно реализовать получение всех страниц API, используя рекурсию и хуки.

Пример
import got from 'got';

async function fetchAllPages(baseUrl) {
  let page = 1;
  const allData = [];

  while (true) {
    const response = await got(baseUrl, {
      searchParams: { page, per_page: 10 },
      hooks: {
        afterResponse: [
          (response) => {
            // предположим, что ссылка на следующую страницу в заголовке Link
            const linkHeader = response.headers['link'];
            // здесь парсинг Link header (упрощённо)
            return response;
          }
        ]
      }
    });

    const data = JSON.parse(response.body);
    allData.push(...data);

    // проверка на последнюю страницу – можно по длине массива
    if (data.length < 10) break;
    page++;
  }

  return allData;
}

fetchAllPages('https://api.github.com/repos/nodejs/node/commits')
  .then(commits => console.log('Всего коммитов:', commits.length))
  .catch(err => console.error(err));
Всего коммитов: 30

Работа с куками (Cookie Jar)

Got поддерживает встроенное хранилище кук через опцию cookieJar.

Пример
import got from 'got';
import { CookieJar } from 'tough-cookie';

const jar = new CookieJar();
const client = got.extend({ cookieJar: jar });

async function login() {
  // запрос, который устанавливает куки сессии
  await client.post('https://httpbin.org/cookies/set?session=abc123');
  const cookies = await jar.getCookies('https://httpbin.org');
  console.log('Сохранённые куки:', cookies);
}

login();
Сохранённые куки: [Cookie: session=abc123]

Типизация ответов с TypeScript

Got хорошо интегрируется с TypeScript. Можно задать ожидаемый тип ответа с помощью дженерика.

Пример
import got from 'got';

interface Post {
  userId: number;
  id: number;
  title: string;
  body: string;
}

async function getPost(id: number): Promise<Post> {
  const response = await got.get<Post>(`https://jsonplaceholder.typicode.com/posts/${id}`);
  return response.body; // тело автоматически распаршено как Post
}

(async () => {
  const post = await getPost(1);
  console.log('Заголовок:', post.title);
})();
Заголовок: sunt aut facere repellat provident occaecati excepturi optio reprehenderit

библиотека Got для HTTP запросов - comments

En
Got js (javascript)