Jokainen Node.js-fetchejä käsittelevä tutoriaali opettaa sinulle await fetch(url) ja jättää siihen. Sitten tuotantosovelluksesi nielee hiljaa 500-virheen, pyyntö roikkuu 90 sekuntia ilman aikakatkaisua, ja vietät perjantai-iltasi debuggaten jotain, minkä olisi pitänyt olla ilmeistä.
Olen rakentanut Thunderbitillä sisäisiä työkaluja ja datalinjoja jo jonkin aikaa, ja voin sanoa tämän: kuilu ”fetch toimii tutoriaalissani” -tilanteen ja ”fetch toimii tuotannossa” -tilanteen välillä on se, missä suurin osa kivusta asuu. Yksi kehittäjä Redditissä sanoi sen täydellisesti: ”kun menet tuotantoon, huomaat tarvitsevasi jotain kestävämpää kuin natiivi fetch.”
Toinen tunnusti: ”Työskentelin web-kehittäjänä 3 vuotta, ja TIL että fetch API:n catch-lohko EI ole HTTP-virheitä varten.” Tämä opas käy läpi viisi asiaa, jotka useimmat tutoriaalit ohittavat — virheansan, AbortController-aikakatkaisut, retry-logiikan, yhteyksien uudelleenkäytön ja sen, milloin fetchin yli kannattaa siirtyä jäsennellyn datan poimintaan. Jos olet joskus nähnyt fetch-kutsun epäonnistuvan hiljaa tuotannossa, tämä on sinulle.

Mikä on Node.js Fetch API?
Node.js Fetch API on sisäänrakennettu, selainyhteensopiva tapa tehdä HTTP-pyyntöjä (GET, POST, PUT, DELETE jne.) Node.js:stä — ilman Axiosin, node-fetchin tai minkään muun paketin asentamista. Jos olet käyttänyt fetch()-funktiota selaimessa, tunnet jo syntaksin. Nyt sama API toimii palvelimella.
Tässä on nopea versiohistoria:
| Virstanpylväs | Node-versio | Mitä tapahtui |
|---|---|---|
| Kokeellinen fetch-lippu | v17.5.0 / v16.15.0 | fetch lisättiin lipun --experimental-fetch taakse |
| Oletusarvoinen globaali fetch | v18.0.0 | Kokeellinen fetch saatavilla globaalisti, Undici-pohjaisena |
| Vakaa fetch | v21.0.0 | Ei enää kokeellinen |
| 2026 tuotannon peruslinja | v22 LTS / v24 LTS | Suositeltu tuotantoon; v20 on nyt EOL |
Kulissien takana Node:n fetch toimii Undicin voimin — se on Node.js:lle suunniteltu korkeasuorituskykyinen HTTP-asiakas. Se ei nojaa vanhempaan sisäänrakennettuun http-moduuliin. Käytännön hyöty: saat modernin, Promise-pohjaisen HTTP-API:n, joka toimii samalla tavalla selainkoodissasi, Express-taustapalvelussa, serverless-funktiossa ja CLI-skripteissä.
Miksi Node.js Fetch API on tärkeä projekteillesi
Ennen Node 18:aa jokainen uusi projekti aloitettiin samalla rituaalilla: npm install axios tai npm install node-fetch. Vuonna 2026, jos projektisi pyörii ylläpidetyssä Node LTS -versiossa, perus-HTTP-pyynnöt eivät vaadi yhtään riippuvuutta. Se on oikea voitto bundle-koossa, toimitusketjun turvallisuudessa ja käyttöönotossa (front-end- ja back-end-kehittäjät viimein jakavat saman API:n).
Tässä tilanteissa natiivi fetch loistaa:
| Skenaario | Miksi natiivi fetch toimii hyvin | Tuotannon varoitus |
|---|---|---|
| Express/Fastify-taustapalvelin kutsuu REST API:eja | Tuttua async/awaitia, ei riippuvuutta | Lisää aikakatkaisu ja response.ok-tarkistus |
| Serverless-funktiot (Lambda, Vercel jne.) | Pieni kylmäkäynnistyksen pinta-ala, ei paketin asennusta | Pidä aikakatkaisu alustan maksimin alapuolella |
| CLI-skriptit ja automaatiot | Yksinkertainen GET/POST ilman projektin asetuksia | Lisää retry/backoff epävarmoille API:eille |
| Webhook-toimitus tai -välitys | Standardit HTTP-metodit ja otsikot | Älä retryä sokeasti ei-idempotentteja POST-pyyntöjä |
| Raportit ja dashboardit | Hyvä JSONin noutamiseen API:eista | Käytä sivutusta ja yhteyspoolia silmukoissa |
| Mikropalvelujen välinen viestintä | Toimii yksinkertaisiin sisäisiin HTTP-kutsuihin | Harkitse Gotiä tai Undicia suoraan retryihin, hookeihin tai HTTP/2:een |
Uusissa Node 22+ -projekteissa natiivi fetch on järkevä oletus — ellei tiedä tarvitsevansa ominaisuuksia, joita se ei tarjoa (interceptorit, sisäänrakennettu retry, HTTP/2 jne.). npm-latausluvut kertovat siirtymävaiheesta: node-fetchilla on yhä noin 144,9 miljoonaa viikkolatausta, mutta suuri osa siitä on perintöä ja transitiivisia riippuvuuksia. Axios on noin 108,6 miljoonassa, Undici noin 106 miljoonassa, Got noin 36 miljoonassa ja Ky noin 5,6 miljoonassa. Suunta on selvä: natiivi fetch on uusi peruslähtökohta, ja kolmannen osapuolen asiakkaat ovat erityistarpeita varten.
Natiivi Fetch vs node-fetch vs Axios vs Got vs Ky: vuoden 2026 päätösmatriisi
Yleisin kysymys, jonka näen kehittäjäfoorumeilla: ”Mitä HTTP-asiakasta minun pitäisi käyttää Node.js:ssä?” Yksi Reddit-käyttäjä kiteytti asian näin: ”miksi tuoda kirjasto…kun kielessä/viitekehyksessä on toiminnallisuus sisäänrakennettuna?” Oikea pointti — mutta vastaus riippuu siitä, mitä tarvitset.

| Ominaisuus | Natiivi fetch | node-fetch v3 | axios | got v15 | ky v2 |
|---|---|---|---|---|---|
| Node.js-versio | ≥18 (suositus 22/24 LTS) | ≥12.20 | Laaja | ≥22 | ≥22 |
| Asennus vaaditaan | Ei | Kyllä | Kyllä | Kyllä | Kyllä |
| ESM + CJS -tuki | Molemmat (globaali) | Vain ESM (v3) | Molemmat | Vain ESM | Vain ESM |
| Automaattinen hylkäys 4xx/5xx-virheissä | Ei | Ei | Kyllä | Kyllä | Kyllä |
| Sisäänrakennettu retry | Ei | Ei | Ei | Kyllä | Kyllä |
| Pyyntöinterceptorit | Ei | Ei | Kyllä | Kyllä (hookit) | Kyllä (hookit) |
| Striimaustuki | Web ReadableStream | Kyllä | Rajoitettu | Vahvat Node-streamit | Fetch-pohjainen |
| Bundle/asennusjälki | 0 KB | ~107 KB, 3 riippuvuutta | ~2,8 MB, 4 riippuvuutta | ~355 KB, 12 riippuvuutta | ~405 KB, 0 riippuvuutta |
| HTTP/2-tuki | Undici-dispatcherin kautta | Ei | Ei | Kyllä | Ei (fetch-wrapper) |
Lyhyt huomio ESM/CJS-ongelmaan: node-fetch v3 on vain ESM, mikä rikkoi monia projekteja, jotka käyttivät require()-kutsua. Natiivi fetch on globaali — se toimii sekä CJS- että ESM-tiedostoissa ilman mitään import-kikkailua. Jos olet jumissa node-fetch v2:ssa CommonJS:n vuoksi, natiivi fetch ratkaisee ongelman kokonaan.
Ja alkuvaiheen vakaushuolista: kyllä, Node 18:n alkuperäisessä fetch-toteutuksessa oli oikeita bugeja. Yksi kehittäjä Redditissä mainitsi: ”Törmäsin äskettäin hurjaan bugiin natiivin node 18 fetchin kanssa, joten jouduin muuttamaan sovelluksemme.” Se oli vuonna 2023. Vuonna 2026, Node 22- ja 24 LTS -versioilla, nuo ongelmat on ratkaistu. Natiivi fetch on tuotantokelpoinen.
Milloin kannattaa pysyä natiivissa fetchissä
Valitse natiivi fetch, kun:
- Projektisi pyörii Node 22 LTS:llä tai Node 24 LTS:llä.
- Pyynnöt ovat suoraviivaisia REST-kutsuja (GET, POST, PUT, DELETE).
- Olet valmis lisäämään pienen wrapperin
response.ok-tarkistukselle, JSON-parsinnalle, aikakatkaisuille ja retrylle. - Haluat nollan riippuvuuksia ja vähemmän toimitusketjuriskejä.
- Arvostat selain- ja palvelin-API:n yhteneväisyyttä.
- Olet serverless- tai edge-ympäristöissä, joissa sisäänrakennetut API:t ovat suositeltuja.
Milloin Axios, Got tai Ky on järkevämpi
Axios on oikea valinta, kun tiimisi nojaa pyyntö/vastaus-interceptoreihin (esim. automaattinen auth-tokenin uusiminen, tenant-otsikot, keskitetty lokitus), kun haluat oletuksena hylkäyksen HTTP-virheistä tai kun tarvitset taaksepäinyhteensopivuutta vanhempien Node-ajoympäristöjen kanssa.
Got on rakennettu korkealle läpimenolle Node-palveluihin, jotka tarvitsevat sisäänrakennettuja retryjä, hookeja, edistyneitä aikakatkaisuvaiheita, streameja, sivutusapuja, Unix socket -tukea, proxy/cache-työnkulkuja tai HTTP/2-tukea. Se on Node-only HTTP-työn sveitsiläinen linkkuveitsi.
Ky on hyvä vaihtoehto, jos pidät fetchin yksinkertaisuudesta mutta haluat vähemmän boilerplatea — se lisää retryt, aikakatkaisut, hookit ja HTTPError-virheen pienessä paketissa ilman riippuvuuksia.
Miten tehdä GET-pyyntöjä Node.js Fetch API:lla
GET-pyyntö async/awaitilla näyttää tältä:
const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
const post = await response.json();
console.log(post.title);
// → "sunt aut facere repellat provident occaecati excepturi optio reprehenderit"
Ja .then()-ketju, jos pidät siitä enemmän:
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => response.json())
.then(post => console.log(post.title))
.catch(error => console.error(error));
Molemmat toimivat. Mutta kumpikaan ei ole vielä tuoteturvallinen (lisää siitä hetken kuluttua).
Response-lukijat, jotka sinun kannattaa tuntea:
| Metodi | Käytä, kun |
|---|---|
response.json() | Palvelin palauttaa JSONia |
response.text() | Palvelin palauttaa HTML:ää, pelkkää tekstiä, CSV:tä, Markdownia |
response.arrayBuffer() | Tarvitset binääridataa (kuvia, tiedostoja) |
response.body | Tarvitset striimaavaa/paloiteltua käsittelyä |
Parempi malli — sellainen, joka oikeasti tarkistaa virheet:
async function getPost(id) {
const response = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status} ${response.statusText}`);
}
return response.json();
}
const post = await getPost(1);
console.log(post.title);
Tuo if (!response.ok)-rivi on ero tutoriaalin ja tuotantokoodin välillä. Ja se vie meidät suurimpaan ansaan.
Miten lähettää POST-pyyntöjä Node.js Fetch API:lla
POST-pyynnöt noudattavat samaa rakennetta — asetat vain metodin, otsikot ja bodyn:
const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
title: 'Node fetch -opas',
body: 'Tuotantofetch tarvitsee virheenkäsittelyn.',
userId: 1,
}),
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const created = await response.json();
console.log(created.id); // → 101
Muiden pyyntötyyppien lähettäminen (PUT, DELETE, PATCH)
PUT, PATCH ja DELETE käyttävät samaa rakennetta, mutta eri method-arvoa:
// PUT — täydellinen korvaus
await fetch('https://jsonplaceholder.typicode.com/posts/1', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id: 1, title: 'Korvattu', body: 'Täydellinen korvaus', userId: 1 }),
});
// PATCH — osittainen päivitys
await fetch('https://jsonplaceholder.typicode.com/posts/1', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: 'Osittainen päivitys' }),
});
// DELETE
await fetch('https://jsonplaceholder.typicode.com/posts/1', {
method: 'DELETE',
});
Expressin body-parser-ansa: Jos POSTaat JSONia Express-palvelimelle ja req.body tulee takaisin undefined-arvona, korjaus on lähes aina tämä: käytä express.json()-middlewarea, älä express.urlencoded()-middlewarea. Palvelin tarvitsee express.json()-middlewarea ennen reittiäsi, jotta se osaa jäsentää Content-Type: application/json -runkoja. Tämä on yksi yleisimmistä Stack Overflow -kysymyksistä Expressistä, ja se yllättää ihmiset kerta toisensa jälkeen.
import express from 'express';
const app = express();
app.use(express.json()); // ← Tätä tarvitset JSON-POST-bodyille
app.post('/api/posts', (req, res) => {
res.json({ received: req.body });
});
fetch()-virheansa, joka rikkoo tuotantosovelluksia

Täältä useimmat tuotannon fetch-bugit ovat peräisin.
fetch() ei hylkää lupaustaan HTTP 4xx- tai 5xx-virheissä. Se hylkää vain verkkotason epäonnistumisissa — DNS-virheet, internet-yhteyden puute, keskeytetyt pyynnöt. Jos palvelin palauttaa 403 Forbidden- tai 500 Internal Server Error -vastauksen, fetch pitää sitä onnistuneena vastauksena. .catch()-lohkoosi ei koskaan päädytä. try/catch-rakenteesi ei koskaan nappaa sitä. Koodisi käsittelee tyytyväisenä kaiken, mitä palvelin palautti.
MDN:n dokumentaatio sanoo tämän selvästi, mutta useimmat tutoriaalit sivuuttavat sen. Lopputulos? Tällainen koodi näyttää hyvältä, mutta nielee virheet hiljaa:
try {
const response = await fetch('https://api.example.com/private');
const data = await response.json(); // ← Tämä suoritetaan jopa 403-tilassa
console.log('Näyttää onnistuneelta:', data);
} catch (error) {
// Tähän päätyvät vain verkkotason epäonnistumiset
console.error('Napattu:', error);
}
Nopea erittely siitä, mitä kukin malli oikeasti nappaa:
| Malli | Nappaa verkkovirheet | Nappaa 4xx/5xx-virheet | Parsii JSONin turvallisesti | Uudelleenkäytettävä |
|---|---|---|---|---|
Raaka .then(res => res.json()) | Kyllä (.catch():n kautta) | Ei | Ei content-type-tarkistusta | Ei |
try/catch + await fetch() | Kyllä | Ei | Ei content-type-tarkistusta | Ei |
Manuaalinen if (!res.ok) jokaisessa kutsussa | Kyllä | Kyllä | Riippuu jokaisesta kutsusta | Osittain |
Oma fetchJSON()-wrapper | Kyllä | Kyllä | Kyllä | Kyllä |
Rakenna uudelleenkäytettävä fetchJSON()-wrapper
Rakenna yksi wrapper. Tuo se kaikkialle. Lopeta if (!response.ok)-tarkistuksen kopioiminen joka tiedostoon:
export class HTTPError extends Error {
constructor(message, { status, statusText, url, body }) {
super(message);
this.name = 'HTTPError';
this.status = status;
this.statusText = statusText;
this.url = url;
this.body = body;
}
}
export async function fetchJSON(url, options = {}) {
const response = await fetch(url, {
headers: {
Accept: 'application/json',
...options.headers,
},
...options,
});
const contentType = response.headers.get('content-type') || '';
const isJSON = contentType.includes('application/json');
const body = isJSON ? await response.json().catch(() => null) : await response.text();
if (!response.ok) {
throw new HTTPError(`HTTP ${response.status} ${response.statusText}`, {
status: response.status,
statusText: response.statusText,
url: response.url,
body,
});
}
return body;
}
Nyt kun palvelin palauttaa 403:n:
try {
const data = await fetchJSON('https://api.example.com/private');
} catch (error) {
if (error instanceof HTTPError) {
console.error(`Palvelin palautti ${error.status}:`, error.body);
} else {
console.error('Verkko- tai muu epäonnistuminen:', error);
}
}
Virhe sisältää tilakoodin, vastausbodyn ja URL:n — kaiken, mitä tarvitset lokitukseen, hälytyksiin tai käyttäjälle näytettäviin viesteihin. Tuo kerran sisään, käytä kaikkialla.
AbortController ja aikakatkaisut: tuotantomalli Node.js Fetch API:lle

Ilman aikakatkaisua fetch-kutsu jää roikkumaan loputtomasti, kun etäpalvelin vaikenee. Express-reittisi estyy. Lambda palaa läpi suoritusbudjettinsa. Skriptisi vain... jää paikalleen.
Tarkistin hakutulosten kärjen: yksikään Node.js-spesifinen fetch-tutoriaali ei käsittele pyynnön peruuttamista tai aikakatkaisuja. Silti aikakatkaisut ovat yksi tärkeimmistä syistä, miksi kehittäjät pitävät Axiosista tai Gotista. Yhden Reddit-keskustelun otsikko on kirjaimellisesti ”Node fetch does not timeout”.
AbortSignal.timeout()-kutsun käyttö (Node 18.11+)
Yksinkertaisin tapa — yksi lisäoptio:
try {
const response = await fetch('https://api.example.com/data', {
signal: AbortSignal.timeout(5000), // 5 sekuntia
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
console.log(data);
} catch (error) {
if (error.name === 'TimeoutError') {
console.error('Pyyntö aikakatkaistiin 5 sekunnin jälkeen.');
} else {
throw error;
}
}
Huomaa: AbortSignal.timeout() heittää TimeoutError-virheen, ei AbortErroria. Tämä on yksityiskohta, jonka jopa jotkut kokeneet kehittäjät ymmärtävät väärin.
Manuaalinen aikakatkaisu AbortControllerilla
Kun tarvitset enemmän hallintaa — tai jos haluat peruuttaa pyynnön käyttäjän toiminnan perusteella, ei vain ajastimen vuoksi:
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);
try {
const response = await fetch('https://api.example.com/data', {
signal: controller.signal,
});
const data = await response.json();
console.log(data);
} catch (error) {
if (error.name === 'AbortError') {
console.error('Pyyntö keskeytettiin manuaalisesti.');
} else {
throw error;
}
} finally {
clearTimeout(timeout);
}
AbortError vs TimeoutError
Tällä erolla on merkitystä lokitukselle ja käyttäjälle näytettäville viesteille:
| Keskeytystapa | Virheen nimi catch-lohkossa |
|---|---|
AbortSignal.timeout(ms) | TimeoutError |
controller.abort() | AbortError |
| DNS-/verkkovirhe | Tyypillisesti TypeError: fetch failed |
Tässä käytännön esimerkki — Express-reitti, joka kutsuu ulkoista API:a ja jonka on vastattava 3 sekunnissa:
app.get('/dashboard', async (req, res, next) => {
try {
const data = await fetchJSON('https://api.example.com/report', {
signal: AbortSignal.timeout(3000),
});
res.json(data);
} catch (error) {
if (error.name === 'TimeoutError') {
res.status(504).json({ error: 'Ylävirran API aikakatkaistiin' });
return;
}
next(error);
}
});
Ilman tätä mallia hidas ylävirran API estäisi koko reittisi siihen asti, kunnes asiakas antaa periksi.
Retry-logiikka ja yhteyksien uudelleenkäyttö: Node.js Fetch API tuotantotasolle
Natiivissa fetchissä ei ole sisäänrakennettua retryä. Verkkohetkellinen häiriö tai ohimenevä 503 tarkoittaa, että pyyntö yksinkertaisesti epäonnistuu. Useimmissa tuotannon lukuoperaatioissa se ei ole hyväksyttävää.
Koostettava retry-wrapper eksponentiaalisella backoffilla
Tämä on tarkoituksella lyhyt — noin 10 riviä varsinaista logiikkaa:
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
export async function fetchWithRetry(url, options = {}, retries = 2) {
for (let attempt = 0; ; attempt++) {
try {
const response = await fetch(url, options);
if (response.ok || ![408, 429, 500, 502, 503, 504].includes(response.status)) {
return response;
}
if (attempt >= retries) return response;
} catch (error) {
if (attempt >= retries) throw error;
}
await wait(250 * 2 ** attempt); // 250 ms, 500 ms, 1000 ms...
}
}
Milloin retryttää ja milloin ei
- Retrytä: Idempotentit GET- ja HEAD-pyynnöt, ohimenevät tilat (408, 429, 500, 502, 503, 504), hetkelliset verkkohäiriöt.
- Älä retrytä: Ei-idempotentteja POST-pyyntöjä, jotka luovat tietueita, veloittavat rahaa tai laukaisevat sivuvaikutuksia — ellei käytössä ole idempotency key -avaimia.
- Kunnioita Retry-Afteria: 429- (rate limit) ja 503-tiloissa tarkista
Retry-After-otsake ennen backoffia.
Jos et halua rakentaa omaa retry-logiikkaa, Ky on kevyt fetch-wrapper, joka lisää retryt, aikakatkaisut, hookit ja HTTPError-virheen valmiiksi — ilman riippuvuuksia.
Yhteyksien uudelleenkäyttö Undicin Agentin ja Poolin avulla
Korkean läpimenon silmukoissa — satojen sivujen scrapauksessa, API:n kutsumisessa erissä, palvelun pollaamisessa — TCP-yhteyksien uudelleenkäyttö säästää huomattavasti aikaa. Jokainen uusi yhteys tarkoittaa uutta DNS-hakua, TCP-kättelyä ja (HTTPS:n tapauksessa) TLS-neuvottelua.
Koska Node:n fetch toimii Undicin päällä, voit välittää oman dispatcherin:
import { Agent } from 'undici';
const agent = new Agent({
keepAliveTimeout: 10_000,
keepAliveMaxTimeout: 60_000,
});
const response = await fetch('https://api.example.com/data', {
dispatcher: agent,
});
Jos haluat vielä enemmän hallintaa tietylle alkuperälle:
import { Pool } from 'undici';
const pool = new Pool('https://api.example.com', { connections: 10 });
const response = await fetch('https://api.example.com/data', {
dispatcher: pool,
});
// Kun olet valmis:
await pool.close();
Undicin README:n benchmarkit osoittavat, että yhteyksien uudelleenkäyttö ja poolaus voivat parantaa läpimenoa dramaattisesti — undici - dispatch pääsi noin 22 234 pyyntöön sekunnissa verrattuna undici - fetch-tulokseen, noin 5 904 pyyntöä sekunnissa, heidän paikallisessa benchmarkissaan. Todelliset luvut vaihtelevat, mutta suunta on selvä: jos teet paljon pyyntöjä samaan alkuperään, poolilla on väliä.
Yksi asia vielä: kuluta tai peruuta vastausbodyt aina. Kuluttamattomat bodyt voivat aiheuttaa resurssivuotoja Node:n HTTP-sisuksissa.
Striimaavat vastaukset Node.js Fetch API:lla
Suuret tiedostolataukset, paloittaiset JSON-syötteet, server-sent events, LLM:n tuloste — nämä ovat tilanteita, joissa odottaminen koko vastauksen valmistumiseen ennen käsittelyä tuhlaa aikaa ja muistia. Striimaus antaa käsitellä dataa sen saapuessa.

Node 18+ sisältää selainyhteensopivan ReadableStream-virran. Näin striimaat rivikohtaisesti erotellun JSON-vastauksen ja käsittelet jokaisen rivin sen saapuessa:
const response = await fetch('https://example.com/large-file.ndjson');
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { value, done } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
let newlineIndex;
while ((newlineIndex = buffer.indexOf('\n')) >= 0) {
const line = buffer.slice(0, newlineIndex).trim();
buffer = buffer.slice(newlineIndex + 1);
if (line) {
const item = JSON.parse(line);
console.log('Käsitelty:', item.id);
}
}
}
Yksinkertaisempaan tekstistriimaukseen (esim. LLM:n tulosteen putkittaminen stdoutiin):
const response = await fetch('https://example.com/stream');
const reader = response.body.getReader();
const decoder = new TextDecoder();
for (;;) {
const { value, done } = await reader.read();
if (done) break;
process.stdout.write(decoder.decode(value, { stream: true }));
}
Striimaus on yksi alue, jossa natiivi fetch ja Got loistavat. Axiosin striimaustuki on rajoitetumpi.
Milloin fetch() törmää rajoihinsa: jäsennelty web-scraping API:lla
Jossain vaiheessa fetch ei ole enää pullonkaula. Todellinen ongelma muuttuu muotoon: ”Minulla on HTML, mitä nyt?”

Fetch on HTTP-asiakas — se hakee bittejä, tekstiä, JSONia tai HTML:ää. Sillä ei ole käsitystä tuotekortista, hinnasta, arvostelusta tai yhteystaulukosta. Jäsenneltyyn web scrapingiin tyypillinen raakakasa näyttää tältä:
fetch()HTML:n lataamiseen- Cheerio (tai vastaava) elementtien valintaan CSS-valitsimilla
- Oma sivutuslogiikka
- JavaScript-renderöinti, kun sivut ovat client-side
- Proxy-/anti-bot-/CAPTCHA-käsittely
- Valitsimien ylläpito joka kerta, kun sivun asettelu muuttuu
Tässä on tyypillinen fetch + Cheerio -esimerkki — noin 15 riviä tuotelistojen scrapaukseen:
import * as cheerio from 'cheerio';
const response = await fetch('https://example-store.com/products');
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const html = await response.text();
const $ = cheerio.load(html);
const products = $('.product-card')
.map((_, el) => ({
name: $(el).find('.product-title').text().trim(),
price: $(el).find('.price').text().trim(),
url: new URL($(el).find('a').attr('href'), response.url).href,
}))
.get();
console.log(products);
Tämä toimii vakailla sivuilla, joiden HTML on ennustettavaa. Se muuttuu nopeasti hauraaksi — JavaScript-renderöity sisältö, muuttuvat luokkanimet, anti-bot-toimet ja sivutus lisäävät kaikki monimutkaisuutta.
Thunderbitin Open API: raakasta HTML:stä jäsenneltyyn dataan yhdellä kutsulla
Tässä kohtaa toisenlainen työkalu tulee hyödylliseksi. Thunderbitillä rakensimme API-kerroksen, joka hoitaa hankalat osat — JavaScript-renderöinnin, anti-bot-suojauksen, asettelumuutokset — jotta voit keskittyä oikeaan dataan.
Distill API (POST /distill): Muuntaa minkä tahansa URL:n puhtaaksi Markdowniksi. Hyödyllinen LLM:ien syöttämiseen, tietopohjien rakentamiseen tai sisällön analyysiin — HTML-jäsentäjää ei tarvita.
Extract API (POST /extract): Määritä JSON Schema, joka kuvaa haluamasi jäsennellyn datan (tuotteen nimi, hinta, arvosana), ja AI poimii sen. Ei CSS-valitsimia, ei rikkoutumista asettelun muuttuessa.
Tässä sama tuotescraping-tehtävä Thunderbitin Extract API:lla — kutsuttuna natiivilla fetchillä:
const response = await fetch('https://openapi.thunderbit.com/openapi/v1/extract', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.THUNDERBIT_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://example-store.com/products',
renderMode: 'basic',
schema: {
type: 'object',
properties: {
products: {
type: 'array',
items: {
type: 'object',
properties: {
name: { type: 'string', description: 'Tuotteen nimi' },
price: { type: 'string', description: 'Näytettävä tuotteen hinta' },
rating: { type: 'number', description: 'Keskimääräinen asiakasarvosana' },
},
required: ['name', 'price'],
},
},
},
required: ['products'],
},
}),
});
if (!response.ok) throw new Error(`Thunderbit API: ${response.status}`);
const result = await response.json();
console.log(result.data);
Vertailu: noin 15 riviä fetch + Cheerioa (sekä hauraat valitsimet) vastaan yksi API-kutsu, joka palauttaa puhdasta JSONia. Eräajoissa Thunderbit tukee jopa 50 URL-osoitetta extract-eräkutsua kohti ja jopa 100 URL-osoitetta distill-eräkutsua kohti.
Thunderbit ei korvaa fetchiä — fetch on kuljetuskerros. Thunderbit on se poimintakerros, johon turvaudut, kun raakaa HTML:n parsintaa on tullut itse ongelma. Jos hinnat kiinnostavat, ilmainen taso tarjoaa 600 API-yksikköä kokeiluun, ja maksulliset paketit alkavat 6 dollarista kuukaudessa. Voit myös tutustua Thunderbit Chrome -laajennukseen koodittomaan poimintaan suoraan selaimessasi.
Lisää jäsennellyistä scrapauksen lähestymistavoista löytyy oppaistamme aiheisiin parhaat datanpoimintatyökalut, miten luoda web scraper ja datan scrapaus verkkosivulta Exceliin, joissa käydään läpi tarkkoja työnkulkuja yksityiskohtaisesti.
Pikaviite: Node.js Fetch API -pikakortti
Tämä osio kannattaa lisätä kirjanmerkkeihin. Palaa siihen, kun tarvitset mallia kopioitavaksi.
| Malli | Pätkä |
|---|---|
| Perus GET | const res = await fetch(url); const data = await res.json(); |
| Perus POST | await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); |
| HTTP-virheen tarkistus | if (!res.ok) throw new Error(\\HTTP ${res.status}\); |
| Aikakatkaisu (yksinkertainen) | await fetch(url, { signal: AbortSignal.timeout(5000) }); |
| Manuaalinen keskeytys | const c = new AbortController(); setTimeout(() => c.abort(), 5000); await fetch(url, { signal: c.signal }); |
| Retry-tilat | Retrytä 408, 429, 500, 502, 503, 504. Älä retrytä POST-pyyntöä sokeasti. |
| JSON-wrapper | Käytä fetchJSON()-funktiota tarkistamaan ok, parsimaan content type ja heittämään HTTPError. |
| Yhteyspooli | import { Pool } from 'undici'; const pool = new Pool(origin, { connections: 10 }); fetch(url, { dispatcher: pool }); |
| Striimaa palat | const reader = res.body.getReader(); loop over await reader.read() |
| Jäsennelty poiminta | Käytä Thunderbit Extract API:a, kun tavoitteena on kentät verkkosivulta, ei raaka HTML. |
Yhteenveto ja tärkeimmät opit
Node.js:n natiivi fetch on tuotantokelpoinen vuonna 2026 — uusia projekteja varten ei tarvita node-fetchiä, eikä oletus-Axios-riippuvuutta tarvita. Mutta pelkkä raaka fetch() ei yksin ole tuotantokelpoinen HTTP-strategia.
Viisi asiaa, jotka useimmat tutoriaalit ohittavat — ja jotka tämä opas käsitteli:
- Virheansa:
fetch()ei heitä 4xx/5xx-tiloissa. Tarkista ainaresponse.oktai käytä wrapperia kutenfetchJSON(). - Aikakatkaisut: Käytä
AbortSignal.timeout()-kutsua yksinkertaisiin tapauksiin.AbortSignal.timeout()heittääTimeoutError-virheen; manuaalinencontroller.abort()heittääAbortError-virheen. - Retry-logiikka: Ei sisäänrakennettu. Lisää eksponentiaalinen backoff idempotenteille pyynnöille ja ohimeneville virheille. Tai käytä Ky:tä fetch-tyylisellä retryllä suoraan.
- Yhteyksien uudelleenkäyttö: Korkean läpimenon silmukoissa käytä Undicin
Agent- taiPool-luokkaadispatcher-option kautta. - Jäsennelty poiminta: Kun tarvitset dataa verkkosivuilta (ei vain raakaa HTML:ää), harkitse Thunderbitin kaltaista poiminta-API:a hauraita CSS-valitsimia ylläpidon sijaan.
Päätösmatriisi yhdessä lauseessa: käytä natiivia fetchiä useimpiin projekteihin, Axiosia interceptoreihin, Gotiä sisäänrakennettuun retryyn ja HTTP/2:een, Ky:tä fetchiin paremmilla oletuksilla, ja Thunderbitin API:a silloin, kun fetch-pohjaiset scraping-skriptisi käyvät liian monimutkaisiksi ylläpitää.
Kokeile Thunderbitiä jäsennellyn datan poimintaan
Kokeile tämän oppaan malleja. Ja jos haluat nähdä, miten Thunderbit hoitaa jäsennellyn poiminnan, ilmainen taso on hyvä paikka aloittaa — tai katso läpikäynti Thunderbitin YouTube-kanavalta.
Kokeile Thunderbitiä AI Web Scrapingiin Get Started Free
UKK
1. Onko fetch sisäänrakennettu Node.js:ään vai pitääkö se asentaa?
Fetch on sisäänrakennettu Node.js 18:aan ja uudempiin — erillistä asennusta ei tarvita. Se vakautui Node 21:ssä ja on täysin tuettu Node 22 LTS- ja Node 24 LTS -versioissa. Vanhemmille Node-versioille voit käyttää node-fetch-npm-pakettia, mutta uusien projektien kannattaa tähdätä ylläpidettyyn LTS-julkaisuun.
2. Heittääkö fetch virheen 404- tai 500-vastauksista?
Ei. Fetch hylkää lupauksensa vain verkkotason epäonnistumisissa (DNS-virheet, ei yhteyttä, keskeytetyt pyynnöt). HTTP-vastaukset kuten 404, 403 ja 500 palautuvat normaalisti, ja response.ok === false. Sinun täytyy tarkistaa response.ok tai response.status eksplisiittisesti — tai käyttää wrapperia kuten tämän oppaan fetchJSON()-funktiota.
3. Miten lisään aikakatkaisun fetchiin Node.js:ssä?
Yksinkertaisin tapa on AbortSignal.timeout(ms), joka on saatavilla Node 18.11+:ssa: await fetch(url, { signal: AbortSignal.timeout(5000) }). Tämä heittää TimeoutError-virheen, jos pyyntö ylittää 5 sekuntia. Jos haluat enemmän kontrollia, luo AbortController käsin ja kutsu controller.abort()-metodia setTimeout-kutsusta. Napaa AbortError manuaalisessa mallissa ja TimeoutError AbortSignal.timeout()-mallissa.
4. Voinko käyttää fetchiä web scrapingiin Node.js:ssä?
Kyllä, mutta fetch palauttaa vain raakaa HTML:ää. Tarvitset parserin, kuten Cheerio, tiettyjen elementtien poimimiseen sekä oman logiikan sivutukseen, JavaScript-renderöityihin sivuihin ja anti-bot-toimiin. Jäsennellyn datan poimintaan laajassa mittakaavassa — kun haluat puhdasta JSONia tuotenimillä, hinnoilla tai yhteystiedoilla — harkitse Thunderbitin Extract API:a, joka käyttää AI:ta palauttamaan jäsenneltyä dataa ilman CSS-valitsimia tai asetteluriippuvaista koodia.
5. Pitäisikö minun vaihtaa Axiosista natiiviin fetchiin vuonna 2026?
Uusissa projekteissa Node 22+:ssa natiivi fetch on vahva oletus. Se on ilman riippuvuuksia, Promise-pohjainen ja jakaa saman API:n selaimen fetchin kanssa. Pidä Axios, jos nojaat pyyntö/vastaus-interceptoreihin, oletusarvoiseen HTTP-virheiden hylkäykseen tai tarvitset taaksepäinyhteensopivuutta vanhempien Node-versioiden kanssa. Molemmat ovat kelvollisia valintoja — päätös riippuu siitä, mitä ominaisuuksia projektisi oikeasti käyttää.
Lue lisää


