Vezérlőpult
13. Hibakezelés · Node felé

4. blokk · Node felé · 13. fejezet

Hibakezelés

Egy szerver naponta ezerszer kap rossz adatot: hiányzó mezőt, hibás JSON-t, nem létező termékazonosítót. Ha ilyenkor a program leáll, mindenki más kérése is elveszik. Ebben a fejezetben megtanulod olvasni a hibaüzenetet és a stack trace-t, elkapni a hibát try / catch-csel, dobni a sajátodat throw-val, és extends Error-ral saját hibaosztályt írni — ez az Express hibakezelő middleware-jének az alapja.

Élő gyakorló11 feladat automatikus ellenőrzéssel≈ 1 óra

Tanulási célok

1Háromféle hiba

FajtaMikor derül kiPéldaMit tehetsz
Szintaktikaia kód le sem fut — a Node már beolvasáskor megállhiányzó zárójel, cosnt, lezáratlan szövegelolvasod a hibaüzenetet (sor:oszlop), javítod
Futásidejűfutás közben, egy adott utasításnálnull.nev, nem létező változó, hibás JSONezt lehet elkapni (try / catch) — ez a fejezet témája
Logikaisoha nem „szól" — csak rossz az eredmény< helyett <=, rossz sorrendű else ifconsole.log-os nyomkövetés, tesztek (mint az ellenőrző itt)

Amikor futásidejű hiba történik, a JavaScript kivételt dob (throw): az aktuális függvény azonnal megszakad, a hiba „felfelé" halad a hívó függvényekhez, és ha senki nem kapja el, a program leáll, Node-ban a terminálba írt hibaüzenettel. A hibakezelés arról szól, hogy te döntsd el, hol és hogyan áll meg ez a folyamat.

2A beépített hibaosztályok

Minden dobott hiba egy objektum, általában az Error osztály (vagy egy leszármazottja) példánya — a 12. fejezet osztályai itt jönnek jól. A hiba neve már sokat elárul arról, mit rontottunk el:

OsztályMit jelentTipikus üzenet
SyntaxErrora kód vagy egy feldolgozott szöveg nem érvényesUnexpected token '}' in JSON at position 12
ReferenceErrornem létező változóra hivatkozol (elgépelés, hatókör)termkek is not defined
TypeErrorrossz típusú értéken végzel műveletet — a leggyakoribbCannot read properties of null (reading 'nev')
termek.brutto is not a function
RangeErroregy érték a megengedett tartományon kívül esikInvalid array length, toFixed() digits argument must be between 0 and 100
Erroraz általános ős — a saját hibáidat ebből dobodamit te írsz: Nincs elég készlet: Egér
const termek = null;
console.log(termek.nev);
// TypeError: Cannot read properties of null (reading 'nev')

console.log(termkek);
// ReferenceError: termkek is not defined

JSON.parse("{rossz");
// SyntaxError: Expected property name or '}' in JSON at position 1
✅ A TypeError-T MINDIG „VISSZAFELÉ" OLVASD

„Cannot read properties of null (reading 'nev')" = valami, amiről azt hitted, objektum, valójában null, és annak a .nev-ét kérted. A kérdés tehát nem a nev, hanem: miért null az a valami? — általában egy find, ami nem talált, vagy egy hiányzó mező.

3A stack trace olvasása

Ha Node-ban egy hibát senki nem kap el, a terminálban ilyet látsz. Ne ijedj meg a mennyiségétől — három részből áll, és a felső kettő a fontos:

$ node index.js
/home/anna/webshop/keszlet.js:4
    throw new Error(`Nincs elég készlet: ${nev}`);
    ^

Error: Nincs elég készlet: Egér
    at kivesz (/home/anna/webshop/keszlet.js:4:11)
    at rendel (/home/anna/webshop/rendeles.js:9:3)
    at file:///home/anna/webshop/index.js:6:1
    at ModuleJob.run (node:internal/modules/esm/module_job:271:25)

Node.js v22.12.0

A módszer: keresd meg a legfelső olyan sort, amely a te fájlodra mutat — ott történt a baj. A lánc többi sora azt mondja el, hogyan jutott oda a program, ami akkor kell, ha a hiba helye rendben van, csak rossz adattal hívták.

4try / catch / finally

A try blokkba kerül a „kockázatos" kód. Ha benne hiba dobódik, a futás azonnal a catch blokkba ugrik (a try hátralévő sorai kimaradnak), és a program utána tovább fut:

try {
  const termek = null;
  console.log(termek.nev);          // itt dobódik a TypeError
  console.log("ez már nem fut le");
} catch (e) {
  console.log("Elkaptuk:", e.message);   // Elkaptuk: Cannot read properties of null (reading 'nev')
}
console.log("A program tovább fut!");

A catch (e) zárójelében kapod meg a hibaobjektumot (gyakori név: e, err, error). Ha nincs rá szükséged, a modern JS-ben el is hagyhatod: catch { … }.

finally — ami mindig lefut

A finally blokk minden esetben lefut: ha nem volt hiba, ha volt és elkaptuk, sőt akkor is, ha a try-ban return áll. Takarításra való: fájl vagy kapcsolat lezárása, „töltés" jelző kikapcsolása.

function feldolgoz(adat) {
  console.log("kapcsolat nyitása");
  try {
    if (!adat) throw new Error("Nincs adat");
    return "kész";
  } catch (e) {
    console.log("hiba:", e.message);
    return "sikertelen";
  } finally {
    console.log("kapcsolat zárása");   // a return után is lefut
  }
}
console.log(feldolgoz(null));
// kapcsolat nyitása → hiba: Nincs adat → kapcsolat zárása → sikertelen
⛔ NE NYELD LE A HIBÁT

Az üres catch (e) {} a legrosszabb: a hiba eltűnik, a program „megy tovább" rossz adattal, és napokkal később derül ki, hogy valami nem stimmel. A catch-ben mindig csinálj valamit: írd ki (console.error), adj vissza értelmes alapértéket, vagy dobd tovább (throw e). És csak azt tedd try-ba, ami tényleg hibázhat — ne az egész programot.

5Az error objektum és a console.error

A catch-ben kapott objektumnak három fontos tulajdonsága van:

TulajdonságMit adPélda
e.namea hiba fajtája (az osztály neve)"TypeError", "Error", "ValidationError"
e.messageaz üzenet — amit a new Error("…")-nak adtál"Nincs elég készlet: Egér"
e.stacka teljes stack trace szövegként (név + üzenet + at sorok)többsoros string
function harmadik() { throw new Error("Valami elromlott"); }
function masodik()  { harmadik(); }
function elso()     { masodik(); }

try {
  elso();
} catch (e) {
  console.log(e.name);                    // Error
  console.log(e.message);                 // Valami elromlott
  console.log(e.stack.split("\n")[0]);    // Error: Valami elromlott
  console.error("HIBA:", e.message);      // a hibacsatornára ír
}

A console.error ugyanúgy néz ki, mint a console.log, de Node-ban a hibakimenetre (stderr) ír, nem a normál kimenetre. Ezért a naplózó és a futtató környezet szét tudja válogatni: a rendes kiírás mehet egy fájlba, a hibák egy másikba vagy riasztásba. Hibát tehát console.error-ral írj ki, ne log-gal. Ha az egész objektumot adod át neki (console.error(e)), Node a stack trace-t is kiírja.

6throw — mikor dobj, mikor adj vissza null-t

Nemcsak a JavaScript dobhat hibát: te is, ha egy függvény olyan adatot kap, amivel nem tud értelmesen dolgozni. A throw new Error("üzenet") azonnal megszakítja a függvényt, és a hiba a hívóhoz kerül:

function oszt(a, b) {
  if (b === 0) {
    throw new Error("Nullával nem osztunk!");   // innen nem fut tovább
  }
  return a / b;
}

try {
  console.log(oszt(10, 0));
} catch (e) {
  console.log(e.message);     // Nullával nem osztunk!
}

Az üzenetbe tedd bele a konkrét értéket, amivel baj van — template literállal: throw new RangeError(`Index kívül esik: ${i}`). Bármelyik beépített osztályt dobhatod (RangeError, TypeError), ha az jobban leírja a bajt; a legtöbbször a sima Error elég.

Guard clause — az őrfeltétel

A rossz eseteket a függvény elején, külön-külön if-ekkel szűrd ki, és dobj vagy térj vissza azonnal. Így a „boldog út" nem kerül egyre mélyebb else ágakba, és minden ellenőrzés egy sor:

function kivesz(egyenleg, osszeg) {
  if (osszeg <= 0)        throw new Error("Érvénytelen összeg");   // őr 1
  if (osszeg > egyenleg)  throw new Error("Nincs elég fedezet");   // őr 2

  return egyenleg - osszeg;                                    // innentől biztos, hogy jók az adatok
}

Dobjak, vagy adjak vissza null-t / false-t?

HelyzetVálaszPélda
A hiányzó eredmény normális, várható kimenetnull / undefined / üres tömbkeresés, ami nem talál: findundefined; üres verem pop()null
Egy művelet sikerült vagy nem, és a hívó úgyis elágazik rátrue / falsekosar.torol("Egér"), szamla.kivesz(500) fedezet nélkül
A bemenet érvénytelen, a függvény nem tud értelmesen folytatnithrownullával osztás, negatív ár, ismeretlen kuponkód, hibás JSON
Rossz elemek egy listában, a többi feldolgozhatókihagyás, nem hibaa nem szám elemek átugrása összegzéskor

Ökölszabály: ha a hívó valószínűleg elfelejtené ellenőrizni a visszaadott null-t, és ebből később TypeError lenne, inkább dobj — egy hangos hiba jobb, mint egy csendes rossz adat.

7Saját hibaosztály

Ha a hívónak különbséget kell tennie a hibák között („ez a felhasználó hibája, az meg a mienk"), az üzenet szövegét vizsgálgatni törékeny. Ehelyett az Error-ból örökölsz egy saját osztályt — pont úgy, mint a 12. fejezetben:

class ValidationError extends Error {
  constructor(uzenet, mezo) {
    super(uzenet);                    // az Error kapja az üzenetet → e.message
    this.name = "ValidationError";    // különben "Error" maradna a neve
    this.mezo = mezo;                 // saját plusz adat
  }
}

function ellenorizTermek(termek) {
  if (!termek.nev)  throw new ValidationError("A név kötelező", "nev");
  if (termek.ar < 0) throw new ValidationError("Az ár nem lehet negatív", "ar");
  return true;
}

instanceof a catch-ben

A catch minden hibát elkap — a tiédet és a véletlen TypeError-t is. Ezért instanceof-fal válogass: a sajátodat kezeled, a többit tovább dobod, hogy ne tűnjön el:

try {
  ellenorizTermek({ nev: "Egér", ar: -5 });
} catch (e) {
  if (e instanceof ValidationError) {
    console.log(`Érvénytelen mező: ${e.mezo} – ${e.message}`);   // Érvénytelen mező: ar – Az ár nem lehet negatív
  } else {
    throw e;                       // nem a miénk → menjen tovább felfelé
  }
}

Egy másik gyakori minta: a catch nem dob, hanem eredményobjektumot ad vissza — { ok: true } vagy { ok: false, mezo: e.mezo, hiba: e.message } — így a hívó egyszerű if-fel dolgozza fel a kimenetet. Az űrlap-validálás pontosan így működik.

⚙️ HOL JÖN ELŐ NODE-BAN?

Az Express 5-ben a route-okban dobott hibák egy központi hibakezelő middleware-be futnak, amelynek négy paramétere van: (err, req, res, next). Ott egyetlen helyen döntöd el a választ a hiba típusa alapján: err instanceof NotFoundError → 404, ValidationError → 400 az err.mezo-val, minden más → 500 és console.error(err). Sok projekt ezért a hibaosztályba tesz egy status mezőt (this.status = 404), és a middleware csak res.status(err.status ?? 500)-t ír. Ugyanezt az extends Error + name + saját mező mintát írod meg itt a feladatokban.

8JSON.parse védése

A 11. fejezetből tudod: a JSON.parse hibás szövegre SyntaxError-t dob. Kívülről (fájl, hálózat, felhasználó) érkező szöveg bármikor lehet hibás, ezért a parse-olás a klasszikus try / catch-eset:

function biztonsagosParse(szoveg) {
  try {
    return JSON.parse(szoveg);
  } catch {
    return null;                 // hibás szöveg → null, a program nem omlik össze
  }
}

console.log(biztonsagosParse('{"nev":"Egér"}'));   // { nev: 'Egér' }
console.log(biztonsagosParse("{rossz"));          // null

Gyakran nem null-t akarsz, hanem a beépített, technikai üzenet helyett egy sajátot — és utána még ellenőrzöd is, hogy a kapott adat a várt alakú-e. Ilyenkor a try-ban csak a parse legyen, a további ellenőrzés a try után — különben a saját hibádat is elkapná ugyanaz a catch:

function jsonOsszeg(szoveg) {
  let adat;
  try {
    adat = JSON.parse(szoveg);
  } catch {
    throw new Error("Hibás JSON");          // saját üzenet a SyntaxError helyett
  }
  if (!Array.isArray(adat)) throw new Error("Nem tömb");   // a try-on kívül
  return adat.reduce((s, x) => s + x, 0);
}
ℹ️ A MINTA NEVE: „FORDÍTÁS"

Elkapod egy alacsony szintű réteg hibáját (JSON, fájl, adatbázis), és a saját szintednek megfelelő hibát dobsz helyette (Hibás JSON, NotFoundError). A hívónak így nem kell ismernie a belső részleteket — Node-ban ez a service-réteg egyik fő dolga.

9Élő kód — próbáld ki!

Dobj és kapj el hibákat, nézd meg a beépített hibafajták üzeneteit, próbáld ki a finally-t és a saját hibaosztályt. Egy el nem kapott hiba itt is úgy jelenik meg, ahogy Node-ban a terminálban: a hiba nevével és üzenetével.

JavaScript-gyakorló

Válassz mintát vagy írj sajátot — majd Futtatás (Ctrl+Enter) ↓

A kimenet itt jelenik meg…

10Feladatok

Függvényeket írsz, amelyeket az ellenőrző különböző argumentumokkal hív. Újdonság: egyes teszteknél a függvénynek hibát kell dobnia — ezeknél a „VÁRT" oszlopban ez áll: „hibát dob ezzel az üzenettel: …", és a teszt csak akkor zöld, ha a dobott hiba message-e karakterre pontosan egyezik (ékezet, írásjel, idézőjel is számít). Ha a függvényed ilyenkor nem dob, hanem visszaad valamit, a „KAPOTT" sor megmutatja, mit. Más feladatoknál épp fordítva: a függvény soha nem dobhat, hanem null-t vagy eredményobjektumot ad vissza.

Gyakorló feladatok

⭐ alap · ⭐⭐ haladó · ⭐⭐⭐ kihívás — a haladásod ebben a böngészőben megmarad.

Kész: 0 / 0
✅ AZ ÜZENET NEM EGYEZIK?

Másold az üzenetet a feladat szövegéből, ne gépeld. Template literálnál figyelj a változó helyére és a körülötte lévő idézőjelekre (`Nem szám: "${szoveg}"`). És a throw előtt ne legyen return — a dobás után már semmi sem fut le a függvényben.

11Összegzés

  1. Háromféle hiba: szintaktikai (le sem fut), futásidejű (kivétel — elkapható), logikai (rossz eredmény, nyomkövetés és teszt).
  2. Beépített osztályok: TypeError (rossz típus, null.nev), ReferenceError (nincs ilyen változó), SyntaxError (hibás kód/JSON), RangeError (tartományon kívül); ős: Error.
  3. Stack trace: első sor = név + üzenet; az at fuggveny (fájl:sor:oszlop) sorok a hívási lánc a hiba helyétől lefelé — a legfelső saját fájlod a gyanús.
  4. try { … } catch (e) { … } finally { … }: hibánál a catch fut, a finally mindig (return után is). Üres catch tilos.
  5. Error objektum: e.name, e.message, e.stack; hibát console.error-ral írj ki (stderr).
  6. throw new Error("üzenet") megszakítja a függvényt; guard clause-okkal a függvény elején. Dobj, ha a bemenet érvénytelen; null/false, ha a „nincs" normális kimenet.
  7. Saját hibaosztály: class ValidationError extends Error + super(uzenet) + this.name + saját mező; a catch-ben instanceof, a többit throw e.
  8. JSON.parse mindig try-ban; csak a parse legyen benne, az ellenőrzés utána — és fordítsd le a hibát a saját szintedre.
➡️ KÖVETKEZŐ LÉPÉS

Az utolsó fejezetben a kódot fájlokra bontod: export / import, a Node modulrendszere, egy mini webshop-projekt fájlszerkezete — majd egy záró feladatsor az egész kurzus anyagából, hibakezeléssel, osztályokkal és tömb-metódusokkal együtt.