Сетевые вызовы в Node.js с помощью Got
Библиотека Got для HTTP запросов в Node.js
Как выполнить базовый GET запрос?
Для начала устанавливается пакет got через npm:
npm install gotGot 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