Express alapok · 3. tananyag
Az előző fejezet végén összegyűlt egy lista arról, mi mindent kellene még megírnunk kézzel. Pontosan ezt kapjuk meg az Expressben. Most feltelepítjük, megírjuk vele az első alkalmazást, és megnézzük, mit vesz le a vállunkról — meg azt is, mit nem.
http moduljához;app.listen() mögött;res.send(), res.json(),
res.status() és res.sendStatus()
metódusokkal — és tudni, melyik mikor való;app.js / index.js), és tudni, miért;Az Express a Node.js legelterjedtebb webes keretrendszere. Nem cseréli le a
beépített http modult — ráépül: ugyanaz a szerver fut
alatta, csak kényelmesebb felülettel.
Egy mondatban: az Express a kérés-válasz kezelés kényelmi rétege.
http moduljára épül, nem helyette van.| Amit ad | Mit jelent a gyakorlatban? |
|---|---|
| Útvonalkezelés | app.get("/api/products/:id", …) — nincs több if-lánc és split("/") |
| Middleware-lánc | Közös lépések (naplózás, hitelesítés) egy helyen, minden végpontra |
| Válasz-segédfüggvények | res.json(), res.status(), res.send() — a Content-Type automatikus |
| Törzs-feldolgozás | express.json() — és már van req.body |
| Statikus fájlok | express.static("public") — egy sor |
| Hibakezelés | Központi hibakezelő, az 5-ösben az aszinkron hibákkal együtt |
| Ökoszisztéma | Több ezer kész middleware: CORS, tömörítés, biztonsági fejlécek, feltöltés |
Ezt legalább ilyen fontos tudni — az Express szándékosan minimalista:
| Amit NEM ad | Mit kell magadnak megoldanod? |
|---|---|
| Adatbázis-kezelés | Külön eszköz kell (nálunk: Prisma, a 3. blokkban) |
| Validáció | Külön könyvtár (nálunk: Zod) vagy kézzel |
| Projektszerkezet | Nincs előírt mappastruktúra — te alakítod ki |
| Hitelesítés | Külön csomagok (JWT, jelszó-hashelés) |
| Sablonozás alapból | Kell hozzá választani egy sablonmotort (mi nem használunk: JSON-t adunk vissza) |
Mindkettő. Az Express nem szól bele, hogyan építed fel a projektet — ez szabadság, de felelősség is. Ezért fogunk a 7. fejezetben rétegekre bontani egy tudatosan kialakított szerkezetet. (Van szigorúbb keretrendszer is: ilyen a Nest.js, amit az 5. blokkban veszünk — az mindent előír.)
Az Express npm-csomag, tehát a projektbe telepítjük:
mkdir express-proba cd express-proba npm init -y npm install express
Ellenőrzés — a package.json kiegészült:
{
"name": "express-proba",
"version": "1.0.0",
"type": "module",
"main": "index.js",
"dependencies": {
"express": "^5.2.1"
}
}
"type": "module" SORT!
Enélkül az import nem működik
(SyntaxError: Cannot use import statement outside a module). Ha
npm init -y volt, ezt kézzel kell hozzáadnod — lásd az
1. blokk 3. fejezetét.
A telepített verzió: Express 5.2.1. A csomag Node 18-as vagy újabb verziót igényel — a mi Node 24-esünk bőven megfelel.
import express from "express"; // 1 const app = express(); // 2 app.get("/", (req, res) => { // 3 res.send("Szia az Expressről!"); // 4 }); app.listen(3000, () => { // 5 console.log("A szerver fut: http://localhost:3000"); });
Indítás:
node --watch index.js
| Sor | Mit csinál? | Miért így? |
|---|---|---|
| 1 | Behúzza az Express csomagot | Nincs node: előtag, mert ez npm-csomag, nem beépített modul |
| 2 | Létrehozza az alkalmazást | Az express() egy függvényt ad vissza, ami egyben objektum is — ehhez adjuk hozzá az útvonalakat |
| 3 | Regisztrál egy útvonalat | „Ha GET kérés jön a / címre, futtasd ezt a függvényt” |
| 4 | Elküldi a választ | A res.send() beállítja a Content-Type-ot és lezárja a választ — nincs külön res.end() |
| 5 | Elindítja a szervert | Ugyanaz, mint a http modulnál: a callback egyszer fut le |
req ÉS A res ISMERŐS
Ugyanaz a két objektum, mint a http modulban — az Express csak
kiegészíti őket új metódusokkal (res.json,
res.status, req.params…). Az eredeti
tulajdonságok (req.method, req.url,
req.headers) továbbra is elérhetők.
app.listen() mögött?Ez a sor a háttérben pontosan ezt csinálja:
// amit az app.listen(3000) valójában tesz: import http from "node:http"; const server = http.createServer(app); // az app maga a kérés-feldolgozó függvény! server.listen(3000);
Az app egy függvény, amely
(req, res) paramétereket vár — pontosan olyan, mint amit a
http.createServer() elvár. Az Express tehát nem helyettesíti a Node
szerverét, hanem kiszolgáló függvényt ad neki.
A 8. fejezetben, amikor tesztelni fogjuk az alkalmazást, pontosan ezt használjuk ki: az
app-ot át tudjuk adni tesztelő eszköznek anélkül, hogy tényleg
elindítanánk egy szervert a 3000-es porton.
Az Express négy metódust ad, amit naponta használni fogsz. A
Content-Type fejlécet mindegyik automatikusan beállítja — de
nem mindegy, melyiket választod.
| Metódus | Mit csinál? | Content-Type |
|---|---|---|
res.send(érték) | Elküldi az értéket; a típusból következtet a formátumra | szövegnél: text/html, objektumnál: application/json |
res.json(objektum) | Kifejezetten JSON-t küld | mindig application/json |
res.status(kód) | Csak beállítja a státuszkódot — láncolni kell utána | — |
res.sendStatus(kód) | Beállítja a kódot és azonnal küld | text/plain |
Ezek valódi, kipróbált válaszok (Express 5.2.1):
| Amit írsz | Státusz | Content-Type | Törzs |
|---|---|---|---|
res.send("Szia!") | 200 | text/html; charset=utf-8 | Szia! |
res.send({ a: 1 }) | 200 | application/json; charset=utf-8 | {"a":1} |
res.send([1, 2, 3]) | 200 | application/json; charset=utf-8 | [1,2,3] |
res.send(42) | 200 | application/json; charset=utf-8 | 42 |
res.json({ a: 1 }) | 200 | application/json; charset=utf-8 | {"a":1} |
res.status(201) | 201 | application/json; charset=utf-8 | {"ok":true} |
res.sendStatus(204) | 204 | — (nincs) | (üres) |
res.sendStatus(404) | 404 | text/plain; charset=utf-8 | Not Found |
Figyeld meg a táblázat két meglepetését. Az egyik, hogy a szám és a tömb is JSON-ként
megy el — csak a szöveg az, ami HTML-nek számít. A másik, hogy a
res.sendStatus(404) az angol Not Found
szöveget küldi törzsként, sima szövegként — ez API-ban nem az, amit a kliens vár.
res.send("szöveg") HTML-T MOND!
Sokan meglepődnek: a Content-Type: text/html, nem
text/plain. Ennek történeti oka van (a böngészőnek szánt tartalom volt
az alapeset). API-nál ezért mindig res.json()-t
használunk, még akkor is, ha csak egy rövid üzenetet küldünk:
res.status(404).json({ error: { message: "Nincs ilyen termék." } });
res.status(201).json(termek); // ✅ helyes — láncolva res.json(termek).status(201); // ❌ hiába: a válasz már elment
A res.status() csak beállít, nem küld. A tényleges
küldés a json() / send() hívásnál történik —
utána már késő módosítani.
A helyes státuszkódok: 200 sikeres lekérésre,
201 létrehozásra (+ Location fejléc),
204 törlésre, 400/422
hibás adatra, 401 és 403 a hitelesítéshez és
jogosultsághoz, 404 nemlétező erőforrásra.
Hat helyzet a webshop API-ból. Válaszd ki, melyik metódussal válaszolnál — ez az a döntés, amit naponta többször meghozol majd.
Ez a szakasz meg fogja spórolni neked az első órák felét. Az interneten és a régebbi könyvekben található példák szinte kivétel nélkül Express 4-esek, és néhány dolog az 5-ösben hibát dob.
*) útvonalRengeteg tutorial így írja a 404-kezelőt:
app.get("*", (req, res) => { // ❌ Express 5-ben HIBA! res.status(404).send("Nincs ilyen oldal"); });
Express 5-ben ez azonnal elszáll, már induláskor — a szerver el sem indul:
PathError [TypeError]: Missing parameter name at index 1: *; visit https://git.new/pathToRegexpError for info
at consumeUntil (…\node_modules\path-to-regexp\dist\index.js:108:27)
…
originalPath: '*'
| A hibaüzenet része | Mit jelent? |
|---|---|
PathError [TypeError] | A hiba saját neve PathError, a típusa viszont TypeError — a Node így írja ki a kettőt együtt |
Missing parameter name | „Hiányzik a paraméter neve” — a jokernek nevet kell adni |
at index 1 | A minta hányadik karakterénél akadt el a feldolgozás |
git.new/pathToRegexpError | A mintákat feldolgozó könyvtár saját magyarázó oldala |
originalPath: '*' | Az a minta, amit te írtál — ebből tudod, melyik sorodat kell javítani |
Miért? Mert az 5-ös verzió új útvonal-értelmezőt használ, amelyben a
* önmagában nem érvényes: minden jokernek nevet kell adni.
A helyes megoldás — kétféleképpen:
// 1) Útvonal nélküli middleware a lánc VÉGÉN — ez a legegyszerűbb app.use((req, res) => { res.status(404).json({ error: { message: "Nincs ilyen végpont." } }); }); // 2) Nevesített joker, ha tényleg kell az útvonal app.get("/{*barmi}", (req, res) => { res.status(404).json({ error: { message: "Nincs ilyen végpont." } }); });
A 404-kezelő helye (a lánc legvégén) a 8. fejezet témája — itt csak a szintaxis a lényeg. Az útvonalminták teljes Express 5-ös szabályrendszere pedig a következő fejezetben jön.
app.del("/api/products/:id", (req, res) => { const id = req.param("id"); res.json(204, { torolve: id }); }); app.get("*", (req, res) => res.status(404).send("Nincs ilyen"));
app.delete("/api/products/:id", (req, res) => { const id = req.params.id; res.sendStatus(204); }); app.use((req, res) => res.status(404).json( { error: { message: "Nincs ilyen végpont." } }));
Négy változás egyetlen példában: app.del →
app.delete, req.param() →
req.params.id, res.json(kód, obj) →
külön hívás, és a joker helyett app.use(). Vedd észre azt is, hogy a
204 mellé nem küldünk törzset — a 204 épp azt jelenti,
hogy nincs tartalom.
| Express 4 | Express 5 | Mit tegyél? |
|---|---|---|
app.del(…) | Megszűnt | app.delete(…) |
req.param("nev") | Megszűnt | req.params.nev, req.query.nev vagy req.body.nev |
res.json(status, obj) | Megszűnt | res.status(status) |
app.get("*") | Hibát dob | app.use(fv) vagy app.get("/{*nev}") |
| Aszinkron hiba elveszett | Automatikusan a hibakezelőhöz megy | Nem kell try/catch minden útvonalba |
body-parser csomag kellett | Beépített: express.json() | Ne telepítsd a body-parser-t |
Express 4-ben ez a kód csendben elszállt, és a kérés örökre lógott:
app.get("/api/products", async (req, res) => { const termekek = await adatbazisbolLekerdez(); // ha ez hibát dob… res.json(termekek); });
Express 5-ben a keretrendszer elkapja, és továbbadja a hibakezelőnek.
Ellenőrizve: egy szándékosan dobott hiba az async útvonalban eljut a
hibakezelő middleware-hez, és 500-as válasz születik belőle:
GET /hiba → 500 application/json {"error":"szandekos hiba"}
try/catch?
De — csak nem mindenhova. Ha csak azt akarod, hogy a hiba
500-as válasz legyen, elég az automatikus továbbadás. Ha viszont
saját üzenetet vagy más státuszkódot szeretnél (például
404-et, ha nincs ilyen termék), akkor kell a
try/catch. A 8. fejezetben ezt
részletesen kidolgozzuk.
Hét kódrészlet régi tutorialokból. Döntsd el mindegyikről, hogy Express 5-ben működik-e. A visszajelzés megadja a javítást is.
app.js és index.jsEddig egyetlen fájlban volt minden. Már most érdemes kettéválasztani:
app.jsMIT TUD?// app.js — az alkalmazás FELÉPÍTÉSE import express from "express"; const app = express(); app.use(express.json()); app.get("/", (req, res) => { res.json({ uzenet: "Szia az Expressről!" }); }); export default app; // NEM indítjuk el
index.jsHOL FUT?// index.js — a szerver INDÍTÁSA import app from "./app.js"; const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log( `A szerver fut: http://localhost:${PORT}`); });
| Ok | Magyarázat |
|---|---|
| Tesztelhetőség | A teszt az app-ot használja, és nem indít szervert — nem foglal portot, gyorsabb, és párhuzamosan is futtatható |
| Egy fájl, egy felelősség | Az app.js azt mondja meg, mit tud az alkalmazás; az index.js azt, hol fut |
| Több indítási mód | Ugyanaz az app futhat éles szerveren, tesztben vagy serverless környezetben |
.js KITERJESZTÉS KÖTELEZŐ
Az importban: from "./app.js". ESM-ben ez nem elhagyható — lásd az
1. blokk 3. fejezetét. Enélkül ERR_MODULE_NOT_FOUND hibát kapsz.
const PORT = process.env.PORT || 3000;
| Rész | Mit csinál? |
|---|---|
process.env | A környezetből olvassa a portot (.env fájlból vagy a szolgáltatótól) |
|| 3000 | Ha nincs megadva, a 3000-est használja — így a projekt beállítás nélkül is elindul |
Használat .env fájllal (1. blokk, 3. fejezet):
# .env PORT=4000
"scripts": { "dev": "node --watch --env-file=.env index.js" }
Mert éles környezetben a szolgáltató mondja meg, melyik porton kell figyelni — jellemzően éppen a
PORT környezeti változóban. Ha beégetnéd, a program ott el sem indulna.
Ráadásul így iskolai gépen is könnyű másik portra váltani, ha a 3000-es foglalt.
| Hibaüzenet / tünet | Mit jelent? | Megoldás |
|---|---|---|
Cannot use import statement outside a module | Hiányzik a "type": "module" | Vedd fel a package.json-ba |
Cannot find module 'express' | Nincs telepítve a csomag | npm install express |
PathError [TypeError]: Missing parameter name at index 1: * | Express 4-es joker útvonal (app.get("*")) | app.use(fv) vagy app.get("/{*nev}") |
EADDRINUSE: address already in use | A port foglalt (fut még egy szerver) | Állítsd le, vagy válts portot a .env-ben |
app.del is not a function | Express 4-es metódus | app.delete(…) |
A válasz text/html, pedig JSON-t vártál | res.send("szöveg") volt használva | res.json({ … }) |
| A státuszkód mindig 200 | res.json(…) sorrend | res.status(201) |
req.body az undefined | Hiányzik az express.json() | app.use(express.json()); — az 5. fejezetben részletesen |
Elméleti
Döntsd el, az Express adja-e készen, vagy külön kell megoldanod:
Melyiket használnád, és miért?
Egy régi tutorialból másoltad ezt. Mi a három hiba, és mi lesz a tünetük?
app.del("/api/products/:id", (req, res) => { const id = req.param("id"); res.json(204, { torolve: id }); }); app.get("*", (req, res) => res.status(404).send("Nincs ilyen"));
Miért érdemes az app.js és az index.js
fájlt szétválasztani? Írj három érvet, és mindegyikhez egy konkrét helyzetet, amikor előny!
Magyarázd el saját szavaiddal, mit jelent az, hogy „az app maga egy
kérés-feldolgozó függvény”. Miért tudja az http.createServer(app)
hívás átvenni? Mihez lesz ez jó a tesztelésnél?
Gyakorlati
Hozz létre egy új projektet, telepítsd az Expresst, és készíts egy szervert, amely a
/ címen JSON-ban visszaadja a nevedet és az aktuális dátumot.
Indítsd npm run dev-vel (node --watch)!
Készíts négy végpontot, amelyek rendre a res.send() szöveggel, a
res.send() objektummal, a res.json() és a
res.sendStatus() metódussal válaszolnak. Nézd meg REST Clienttel
mindegyik Content-Type fejlécét és státuszkódját,
és készíts belőle egy táblázatot!
Készíts végpontokat, amelyek rendre 200, 201,
204, 404 és 500
kóddal válaszolnak, egységes JSON hibaformátummal. Próbáld ki mindet, és jegyezd fel az eredményt!
Vedd ki a portot .env fájlba, és állítsd be a
dev scriptet --env-file-lal. Bizonyítsd be,
hogy a .env átírásával másik porton indul a szerver!
Bontsd szét a projektedet app.js és index.js
fájlra a 6. szakasz szerint. Ellenőrizd, hogy minden ugyanúgy működik. Magyarázd el egy mondatban,
mit tartalmaz a két fájl!
http modult, hanem
ráépül: kényelmi réteg a kérés-válasz kezeléshez.app maga egy kérés-feldolgozó függvény — ezért
tudja az http.createServer(app) átvenni. Ez teszi lehetővé a
tesztelést szerverindítás nélkül.res.json()-t használunk, mert a
res.send("szöveg") text/html fejléccel
megy el.res.status(201).json(…) — fordítva már késő.app.del, req.param(),
res.json(status, obj), és az app.get("*")
hibát dob. Cserébe az aszinkron hibákat automatikusan
továbbítja a hibakezelőnek.app.js) és a szerver indítását
(index.js) szétválasztjuk, a portot pedig
környezeti változóból vesszük.| Fogalom | Jelentés |
|---|---|
| Express | A Node.js legelterjedtebb webes keretrendszere; a http modulra épül |
| Keretrendszer (framework) | Kész szerkezet és eszközkészlet, amelybe a saját kódodat illeszted |
express() | Létrehozza az alkalmazásobjektumot |
app | Maga a kérés-feldolgozó függvény — ezt kapja meg a Node szervere |
app.get(útvonal, kezelő) | Útvonal regisztrálása egy metódusra |
app.listen(port, fv) | A szerver elindítása |
res.send() | Válasz küldése; a formátumot a típusból következteti |
res.json() | JSON-válasz küldése — API-nál ezt használjuk |
res.status(kód) | Státuszkód beállítása; láncolni kell egy küldő metódussal |
res.sendStatus(kód) | Státuszkód beállítása és azonnali küldés, törzs nélkül |
process.env | A környezeti változók objektuma |