Node.js:n Fetch API: Mitä jokainen ohje jättää kertomatta (opas 2026)

Viimeksi päivitetty May 14, 2026
Node.js:n Fetch API: Mitä jokainen ohje jättää kertomatta (opas 2026)
AI-yhteenveto
Lopeta perus fetch-kutsuihin nojaaminen tuotannossa. Opi käsittelemään aikakatkaisuja, HTTP-virheitä, uudelleenkokeilua ja yhteyksien poolausta Node.js:ssä vuonna 2026.

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.

nodejs-fetch-error-handling-flowchart.png

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äsNode-versioMitä tapahtui
Kokeellinen fetch-lippuv17.5.0 / v16.15.0fetch lisättiin lipun --experimental-fetch taakse
Oletusarvoinen globaali fetchv18.0.0Kokeellinen fetch saatavilla globaalisti, Undici-pohjaisena
Vakaa fetchv21.0.0Ei enää kokeellinen
2026 tuotannon peruslinjav22 LTS / v24 LTSSuositeltu 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:

SkenaarioMiksi natiivi fetch toimii hyvinTuotannon varoitus
Express/Fastify-taustapalvelin kutsuu REST API:ejaTuttua async/awaitia, ei riippuvuuttaLisää aikakatkaisu ja response.ok-tarkistus
Serverless-funktiot (Lambda, Vercel jne.)Pieni kylmäkäynnistyksen pinta-ala, ei paketin asennustaPidä aikakatkaisu alustan maksimin alapuolella
CLI-skriptit ja automaatiotYksinkertainen GET/POST ilman projektin asetuksiaLisää retry/backoff epävarmoille API:eille
Webhook-toimitus tai -välitysStandardit HTTP-metodit ja otsikotÄlä retryä sokeasti ei-idempotentteja POST-pyyntöjä
Raportit ja dashboarditHyvä JSONin noutamiseen API:eistaKäytä sivutusta ja yhteyspoolia silmukoissa
Mikropalvelujen välinen viestintäToimii yksinkertaisiin sisäisiin HTTP-kutsuihinHarkitse 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.

http-client-libraries-comparison.png

OminaisuusNatiivi fetchnode-fetch v3axiosgot v15ky v2
Node.js-versio≥18 (suositus 22/24 LTS)≥12.20Laaja≥22≥22
Asennus vaaditaanEiKylläKylläKylläKyllä
ESM + CJS -tukiMolemmat (globaali)Vain ESM (v3)MolemmatVain ESMVain ESM
Automaattinen hylkäys 4xx/5xx-virheissäEiEiKylläKylläKyllä
Sisäänrakennettu retryEiEiEiKylläKyllä
PyyntöinterceptoritEiEiKylläKyllä (hookit)Kyllä (hookit)
StriimaustukiWeb ReadableStreamKylläRajoitettuVahvat Node-streamitFetch-pohjainen
Bundle/asennusjälki0 KB~107 KB, 3 riippuvuutta~2,8 MB, 4 riippuvuutta~355 KB, 12 riippuvuutta~405 KB, 0 riippuvuutta
HTTP/2-tukiUndici-dispatcherin kauttaEiEiKyllä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:

MetodiKä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.bodyTarvitset 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

fetch-error-handling-flowchart.png

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:

MalliNappaa verkkovirheetNappaa 4xx/5xx-virheetParsii JSONin turvallisestiUudelleenkäytettävä
Raaka .then(res => res.json())Kyllä (.catch():n kautta)EiEi content-type-tarkistustaEi
try/catch + await fetch()KylläEiEi content-type-tarkistustaEi
Manuaalinen if (!res.ok) jokaisessa kutsussaKylläKylläRiippuu jokaisesta kutsustaOsittain
Oma fetchJSON()-wrapperKyllä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

request-retry-pooling-flowchart.png

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:

KeskeytystapaVirheen nimi catch-lohkossa
AbortSignal.timeout(ms)TimeoutError
controller.abort()AbortError
DNS-/verkkovirheTyypillisesti 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.

streaming-data-chunking-process.png

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?”

web-scraping-vs-ai-extraction-api-comparison.png

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ä:

  1. fetch() HTML:n lataamiseen
  2. Cheerio (tai vastaava) elementtien valintaan CSS-valitsimilla
  3. Oma sivutuslogiikka
  4. JavaScript-renderöinti, kun sivut ovat client-side
  5. Proxy-/anti-bot-/CAPTCHA-käsittely
  6. 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.

MalliPätkä
Perus GETconst res = await fetch(url); const data = await res.json();
Perus POSTawait fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) });
HTTP-virheen tarkistusif (!res.ok) throw new Error(\\HTTP ${res.status}\);
Aikakatkaisu (yksinkertainen)await fetch(url, { signal: AbortSignal.timeout(5000) });
Manuaalinen keskeytysconst c = new AbortController(); setTimeout(() => c.abort(), 5000); await fetch(url, { signal: c.signal });
Retry-tilatRetrytä 408, 429, 500, 502, 503, 504. Älä retrytä POST-pyyntöä sokeasti.
JSON-wrapperKäytä fetchJSON()-funktiota tarkistamaan ok, parsimaan content type ja heittämään HTTPError.
Yhteyspooliimport { Pool } from 'undici'; const pool = new Pool(origin, { connections: 10 }); fetch(url, { dispatcher: pool });
Striimaa palatconst reader = res.body.getReader(); loop over await reader.read()
Jäsennelty poimintaKä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:

  1. Virheansa: fetch() ei heitä 4xx/5xx-tiloissa. Tarkista aina response.ok tai käytä wrapperia kuten fetchJSON().
  2. Aikakatkaisut: Käytä AbortSignal.timeout()-kutsua yksinkertaisiin tapauksiin. AbortSignal.timeout() heittää TimeoutError-virheen; manuaalinen controller.abort() heittää AbortError-virheen.
  3. Retry-logiikka: Ei sisäänrakennettu. Lisää eksponentiaalinen backoff idempotenteille pyynnöille ja ohimeneville virheille. Tai käytä Ky:tä fetch-tyylisellä retryllä suoraan.
  4. Yhteyksien uudelleenkäyttö: Korkean läpimenon silmukoissa käytä Undicin Agent- tai Pool-luokkaa dispatcher-option kautta.
  5. 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ää

Fawad Khan
Fawad Khan
Fawad kirjoittaa työkseen, ja rehellisesti sanottuna hän jopa pitää siitä. Hän on käyttänyt vuosia selvittääkseen, mikä tekee mainostekstistä vaikuttavaa — ja mikä saa lukijat selaamaan ohi. Kysy häneltä markkinoinnista, niin hän puhuu tuntikausia. Kysy häneltä carbonarasta, niin hän puhuu vielä pidempään.
Sisällysluettelo

Poimi verkkosivu pelkällä pyynnöllä

Kerro mitä tarvitset selkeällä englannilla. Tai vielä parempaa, älä kerro mitään.

Kokeile Thunderbitiä ilmaista
Poimi dataa AI:n avulla
Siirrä data helposti Google Sheetiin, Airtableen tai Notioniin
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week