Undici — это библиотека HTTP-клиента, на которой работает fetch API в Node.js. Она написана с нуля и не опирается на встроенный HTTP-клиент Node.js. В ней есть ряд возможностей, которые делают её хорошим выбором для высокопроизводительных приложений.
Информацию о соответствии Undici спецификации см. в документации Undici.
async function main() {
// Как и в браузерном fetch API, метод по умолчанию — GET
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
const data = await response.json();
console.log(data);
// вернёт что-то вроде:
// {
// userId: 1,
// id: 1,
// title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',
// body: 'quia et suscipit\n' +
// 'suscipit recusandae consequuntur expedita et cum\n' +
// 'reprehenderit molestiae ut ut quas totam\n' +
// 'nostrum rerum est autem sunt rem eveniet architecto'
// }
}
main().catch(console.error);// Данные, отправляемые с клиента на сервер
const body = {
title: 'foo',
body: 'bar',
userId: 1,
};
async function main() {
const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: {
'User-Agent': 'undici-stream-example',
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
const data = await response.json();
console.log(data);
// вернёт что-то вроде:
// { title: 'foo', body: 'bar', userId: 1, id: 101 }
}
main().catch(console.error);Undici позволяет настраивать Fetch API, передавая опции в функцию fetch. Например, можно задать пользовательские заголовки, указать метод запроса и тело запроса. Вот пример того, как настроить Fetch API с помощью Undici:
Функция fetch принимает два аргумента: URL для запроса и объект опций. Объект опций — это объект настроек, с помощью которого можно настроить запрос. Функция возвращает Promise, который резолвится в объект Response.
В следующем примере мы отправляем POST-запрос к API Ollama с JSON-полезной нагрузкой. Ollama — это CLI-инструмент, позволяющий запускать LLM (большие языковые модели) на локальной машине. Скачать его можно здесь.
ollama run mistralЭта команда скачает модель mistral и запустит её на вашей локальной машине.
С помощью пула (pool) можно переиспользовать соединения с одним и тем же сервером, что повышает производительность. Вот пример использования пула с Undici:
import { Pool } from 'undici';
const ollamaPool = new Pool('http://localhost:11434', {
connections: 10,
});
/**
* Стримит завершение (completion) промпта через API Ollama.
* @param {string} prompt - Промпт, который нужно завершить.
* @link https://github.com/ollama/ollama/blob/main/docs/api.md
**/
async function streamOllamaCompletion(prompt) {
const { statusCode, body } = await ollamaPool.request({
path: '/api/generate',
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ prompt, model: 'mistral' }),
});
// Про коды состояния HTTP можно почитать здесь: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
// 200 означает, что запрос выполнен успешно.
if (statusCode !== 200) {
// Тело ответа обязательно нужно вычитать: https://undici.nodejs.org/#/?id=garbage-collection
await body.dump();
throw new Error(`Ollama request failed with status ${statusCode}`);
}
let partial = '';
const decoder = new TextDecoder();
for await (const chunk of body) {
partial += decoder.decode(chunk, { stream: true });
console.log(partial);
}
console.log('Streaming complete.');
}
try {
await streamOllamaCompletion('What is recursion?');
} catch (error) {
console.error('Error calling Ollama:', error);
} finally {
console.log('Closing Ollama pool.');
ollamaPool.close();
}Потоки (streams) — это возможность Node.js, позволяющая читать и записывать данные по частям (чанкам).
import { Writable } from 'node:stream';
import { stream } from 'undici';
async function fetchGitHubRepos() {
const url = 'https://api.github.com/users/nodejs/repos';
await stream(
url,
{
method: 'GET',
headers: {
'User-Agent': 'undici-stream-example',
Accept: 'application/json',
},
},
res => {
let buffer = '';
return new Writable({
write(chunk, encoding, callback) {
buffer += chunk.toString();
callback();
},
final(callback) {
try {
const json = JSON.parse(buffer);
console.log(
'Repository Names:',
json.map(repo => repo.name)
);
} catch (error) {
console.error('Error parsing JSON:', error);
}
console.log('Stream processing completed.');
console.log(`Response status: ${res.statusCode}`);
callback();
},
});
}
);
}
fetchGitHubRepos().catch(console.error);