Vezérlőpult
4. Útvonalak (routing) · Express alapok

Express alapok · 4. tananyag

Útvonalak (routing)

Egy webshop API-nak tucatnyi végpontja van, és ezek nagy része nem fix cím, hanem minta: a /api/products/42 és a /api/products/1337 ugyanaz a végpont. Ez a fejezet arról szól, hogyan válogatja szét az Express a kéréseket.

3 tanóraExpress 5 mintaszintaxisRouter

A fejezet végén tudni fogod

EGY FIGYELMEZTETÉS ELŐRE

Az útvonalminták szintaxisa az Express 5-ben megváltozott. A neten talált példák jó része itt már hibát dob — ezért a 6. szakaszban külön végigmegyünk rajta.

1Mi az útvonal?

Egy útvonal (route) három részből áll:

app.get("/api/products/:id", (req, res) => { … }) HTTP-METÓDUS milyen kéréssel ÚTVONALMINTA melyik címre KEZELŐFÜGGVÉNY mit tegyünk A BEÉRKEZŐ KÉRÉS GET /api/products/42 → a metódus is és a minta is illeszkedik → a kezelő lefut, req.params.id === "42"
1. ábra — Az útvonal három része, és hogy mi kell az illeszkedéshez.

Az Express minden beérkező kérésnél végigmegy a regisztrált útvonalakon, és megkeresi az elsőt, amelyiknél a metódus is és az útvonal is illeszkedik. Ha talál ilyet, meghívja a kezelőfüggvényt. Ha nem talál, a beépített 404-es válasz megy vissza.

EMLÉKEZTETŐ AZ 1. BLOKK 2. FEJEZETÉBŐL

A metódus és az erőforrás együtt írja le a szándékot: GET /api/products = „add ide a termékeket”, DELETE /api/products/42 = „töröld a 42-est”. Ugyanaz az útvonal más metódussal más művelet.

Metódusonként egy függvény

MetódusExpress-hívásMire használjuk a webshopban?
GETapp.get(minta, fv)Lekérdezés: terméklista, egy termék
POSTapp.post(minta, fv)Létrehozás: új termék, új rendelés
PUTapp.put(minta, fv)Teljes felülírás: a termék minden mezője
PATCHapp.patch(minta, fv)Részleges módosítás: csak az ár
DELETEapp.delete(minta, fv)Törlés
MIÉRT app.delete ÉS NEM app.del?

Mert a delete a JavaScriptben ugyan kulcsszó, de metódusnévként használható (app.delete(...)) — csak önálló változónévként nem lenne az. Régi példákban néha app.del szerepel: ez már nem létezik.

A webshop végponttérképe így néz ki:

app.get("/api/products", …);         // összes termék
app.get("/api/products/:id", …);     // egy termék
app.post("/api/products", …);        // új termék
app.put("/api/products/:id", …);     // teljes felülírás
app.patch("/api/products/:id", …);   // részleges módosítás
app.delete("/api/products/:id", …);  // törlés

Figyeld meg: kétféle útvonalminta van összesen (/api/products és /api/products/:id), de hat végpont — mert a metódus is a végpont része.

2Az útvonalak sorrendje számít

Az Express fentről lefelé haladva keresi az első illeszkedő útvonalat, és megáll az elsőnél. Ez a legtöbb kezdő hiba forrása.

Nézd meg ezt a kódot, és tippelj: mit ad vissza a GET /api/products/uj kérés?

app.get("/api/products/:id", (req, res) => {
  res.json({ talalat: "egy termék", id: req.params.id });
});

app.get("/api/products/uj", (req, res) => {
  res.json({ talalat: "űrlap új termékhez" });
});

Kipróbálva (Express 5.2.1):

GET /api/products/uj  →  200  {"talalat":"egy termék","id":"uj"}

A második útvonal soha nem fut le. A :id minta ugyanis az uj szóra is illeszkedik — a paraméter értéke egyszerűen "uj" lesz. Nincs hibaüzenet, nincs figyelmeztetés: a végpont csendben halott.

GET /api/products/uj — ugyanaz a kérés mindkét oldalon ✗ ROSSZ SORREND app.get("/api/products/:id", …) 1. vizsgálat — ILLESZKEDIK, itt megáll app.get("/api/products/uj", …) ide már el sem jut A :id ág fut le {"talalat":"egy termék","id":"uj"} Nincs hibaüzenet. A második végpont csendben halott marad. ✓ JÓ SORREND app.get("/api/products/uj", …) 1. vizsgálat — ILLESZKEDIK, itt megáll app.get("/api/products/:id", …) más azonosítóknál ide jut el A konkrét ág fut le {"talalat":"űrlap új termékhez"} A szabály: a szűkebb minta kerül előre.
2. ábra — Az első illeszkedő útvonal nyer. Ez a fejezet legfontosabb szabálya.

A megoldás: a konkrét minta kerüljön a rugalmas elé.

app.get("/api/products/uj", …);     // ✅ előbb a konkrét
app.get("/api/products/:id", …);    // ✅ utána a paraméteres
SZABÁLY

Ha két útvonalminta ugyanarra a címre illeszkedhet, mindig a szűkebb kerül előre. A gyakorlatban: fix szövegek előbb, paraméteres minták utánuk, a mindent elkapó minták (404-kezelő) legvégül.

Ugyanez metódussal

A metódus is része az illeszkedésnek, ezért ez a két sor nem ütközik:

app.get("/api/products/:id", …);      // lekérdezés
app.delete("/api/products/:id", …);   // törlés

Ugyanaz a minta, más metódus — más végpont.

Melyik kezelő fut le?

Adott egy útvonallista abban a sorrendben, ahogy a kódban szerepel, és egy beérkező kérés. Válaszd ki, melyik kezelőfüggvény fut le! Figyelj a metódusra és a sorrendre is.

1 / 6Helyes: 0
…

3Útvonalparaméterek — :id

A kettősponttal kezdődő szakasz paraméter: bármire illeszkedik, ami nem tartalmaz perjelet, és az értéke a req.params objektumba kerül.

app.get("/api/products/:id", (req, res) => {
  console.log(req.params);          // { id: "42" }
  res.json({ kert: req.params.id });
});
Kérésreq.params
GET /api/products/42{ id: "42" }
GET /api/products/uj{ id: "uj" }
GET /api/products/abc-123{ id: "abc-123" }

Több paraméter egy útvonalban

app.get("/api/categories/:catId/products/:prodId", (req, res) => {
  res.json(req.params);
});
GET /api/categories/3/products/9   →   {"catId":"3","prodId":"9"}

A paraméter nevét te választod meg — a req.params kulcsai ezek a nevek lesznek. Írd őket beszédesen: :id egyértelmű, ha csak egy van, de két azonosítónál :catId és :prodId sokkal olvashatóbb.

Veszély — a paraméter mindig szöveg

Ez a fejezet legfontosabb buktatója:

app.get("/api/products/:id", (req, res) => {
  const id = req.params.id;                    // "42" — SZÖVEG!

  const termek = termekek.find(t => t.id === id);   // ❌ soha nem talál semmit
});

A tömbben az id szám (42), a paraméterben szöveg ("42"), és a === a típust is nézi: "42" === 42 → false.

A helyes megoldás:

app.get("/api/products/:id", (req, res) => {
  const id = Number(req.params.id);

  if (Number.isNaN(id)) {
    return res.status(400).json({ error: { message: "Az azonosító csak szám lehet." } });
  }

  const termek = termekek.find(t => t.id === id);

  if (!termek) {
    return res.status(404).json({ error: { message: "Nincs ilyen termék." } });
  }

  res.json(termek);
});
LépésMiért kell?
Number(...)Mert a req.params értéke szöveg, az adataink azonosítója viszont szám
Number.isNaN(id) ellenőrzésMert a /api/products/alma is illeszkedik a mintára — a Number("alma") pedig NaN
400 a rossz formátumraA kérés hibás volt, nem az erőforrás hiányzik
404 a nem létező termékreA kérés rendben volt, csak nincs ilyen elem
return a válasz előttHogy a függvény ne fusson tovább — lásd az előző fejezetet
MIÉRT Number() ÉS NEM parseInt()?

A parseInt("42abc") eredménye 42 — vagyis elfogadna hibás azonosítót is. A Number("42abc") viszont NaN, tehát elbukik a vizsgálaton. Szigorúbb, és itt épp ezt akarjuk.

4Query paraméterek — ?page=2

Amíg az útvonalparaméter azt mondja meg, melyik erőforrásról van szó, a query string azt, hogy hogyan kérjük: szűrés, rendezés, lapozás.

app.get("/api/products", (req, res) => {
  console.log(req.query);
  res.json(termekek);
});
GET /api/products?page=2&limit=10   →   req.query = { page: "2", limit: "10" }

Amit tudni kell róla

EsetKérésreq.query értéke
Egyszerű érték?page=2{ page: "2" } — szöveg
Hiányzó paraméter? (semmi){} — a req.query.page undefined
Érték nélküli kulcs?akcio{ akcio: "" } — üres szöveg, nem true!
Ugyanaz a kulcs többször?b=x&b=y{ b: ["x", "y"] } — tömb!

Ez a négy sor mind valódi, kipróbált eredmény Express 5.2.1-en. A harmadik és a negyedik sor az, ami meglepetést szokott okozni.

GET /api/products/42?page=2&akcio=true req.params MELYIK erőforrás — az útvonal része { id: "42" } kötelező — enélkül nem illeszkedik az útvonal req.query HOGYAN kérem — szűrés, lapozás { page: "2", akcio: "true" } elhagyható — mindig lehet hiányzó
3. ábra — Egy URL, két objektum. Mindkettőben minden érték szöveg.

Alapértékek és típuskonverzió

Mivel minden szövegként érkezik, és bármelyik hiányozhat, a gyakorlatban így dolgozunk vele:

app.get("/api/products", (req, res) => {
  const oldal = Number(req.query.page) || 1;      // hiányzik vagy nem szám → 1
  const meret = Number(req.query.limit) || 20;    // hiányzik vagy nem szám → 20
  const kereses = req.query.q ?? "";              // hiányzik → üres szöveg

  // …szűrés és lapozás…
});
ÍrásmódMit csinál?Mikor használd?
Number(x) || alapertekHa az eredmény NaN vagy 0, az alapértéket adjaSzámoknál, ahol a 0 úgysem érvényes (oldalszám, elemszám)
x ?? alapertekCsak akkor ad alapértéket, ha x értéke null vagy undefinedSzövegeknél, ahol az üres szöveg érvényes érték lehet
VIGYÁZZ A || OPERÁTORRAL!

A Number("0") || 20 eredménye 20, mert a 0 hamis értéknek számít. Oldalszámnál ez nem gond (nincs 0. oldal), de ha egy paraméternél a 0 érvényes érték, akkor a ?? a helyes választás.

Kapcsolók kezelése

Mivel a ?akcio üres szöveget ad, logikai kapcsolót így vizsgálunk:

const csakAkcios = req.query.akcio === "true" || req.query.akcio === "";

Vagy — tisztább, ha megköveteljük az értéket (?akcio=true):

const csakAkcios = req.query.akcio === "true";
DÖNTSD EL, ÉS ÍRD LE

Az API dokumentációjában rögzítsd, hogy ?akcio=true alakot vársz. Ez pontosan az a „szerződés”, amiről az 1. blokk 2. fejezetében volt szó.

5req.params vs. req.query — mikor melyik?

req.paramsreq.query
Honnan jön?Az útvonalból: /products/42A query stringből: ?page=2
Mit fejez ki?Melyik erőforrásHogyan kérem (szűrés, lapozás, rendezés)
Kötelező?Igen — enélkül nem illeszkedik az útvonalNem — mindig lehet hiányzó
TípusaMindig szövegSzöveg vagy szövegtömb

Ökölszabály: ha az adat nélkül a végpontnak nincs értelme, útvonalparaméter. Ha csak finomítja az eredményt, query paraméter.

GET /api/products/42            ✅  a 42 nélkül nincs mit visszaadni  → útvonalparaméter
GET /api/products?categoryId=3  ✅  nélküle is van válasz (minden termék) → query
GET /api/products/42?mezok=nev  ✅  a kettő egyszerre is használható

Útvonalparaméter vagy query?

Hat adat a webshop API-ból. Döntsd el mindegyikről, hogy az útvonalba vagy a query stringbe való — ez az API tervezésének egyik alapdöntése.

1 / 6Helyes: 0
…

6Útvonalminták Express 5-ben

Itt tér el leglátványosabban az Express 5 a 4-estől. A minták feldolgozását végző könyvtár (path-to-regexp) nagyot változott, és több régi írásmód hibát dob indításkor.

Ami már NEM működik

Régi (Express 4) írásmódMit jelentett?Express 5-ben
app.get("*", …)minden útvonalPathError
app.get("/files/*", …)minden a /files/ alattPathError
app.get("/users/:id?", …)opcionális paraméterPathError
app.get("/:id(\d+)", …)csak számokra illeszkedő paraméterPathError
app.get("/ab+cd", …)ismétlődő betűPathError

A hibaüzenetek (valódi, kipróbált kimenet):

PathError [TypeError]: Missing parameter name at index 1: *; visit https://git.new/pathToRegexpError for info
PathError [TypeError]: Unexpected ? at index 10: /users/:id?; visit https://git.new/pathToRegexpError for info
PathError [TypeError]: Unexpected ( at index 4: /:id(\d+); visit https://git.new/pathToRegexpError for info
JÓ HÍR: EZ INDÍTÁSKOR DERÜL KI

A szerver el sem indul, nem pedig futás közben romlik el. Ha ezt látod, egyetlen sort kell átírnod — a hibaüzenet meg is mondja, hányadik karakternél akadt el, és hogy melyik mintát írtad. A PathError [TypeError] írásmód azt jelenti, hogy a hiba saját neve PathError, a típusa pedig TypeError — a Node így írja ki a kettőt együtt.

Ami helyette van

FeladatExpress 5-ös írásmódMegjegyzés
Minden útvonalapp.use((req, res) => …)Mintát meg sem adunk — a 404-kezelő így készül
Minden a /files/ alattapp.get("/files/*splat", …)A * után nevet kell adni
Opcionális szakaszapp.get("/users{/:id}", …)A kapcsos zárójel opcionális részt jelöl
Csak számokra illeszkedőNincs mintában — a kezelőben ellenőrizzükAhogy a 3. szakaszban láttuk

A nevesített joker (*splat)

app.get("/files/*splat", (req, res) => {
  res.json({ params: req.params });
});

Kipróbálva:

GET /files/kepek/2026/logo.png
→ {"params":{"splat":["kepek","2026","logo.png"]}}
FIGYELEM — A splat ÉRTÉKE TÖMB, NEM SZÖVEG!

A szakaszokra bontott útvonalat kapod meg. Ha egyben kell, fűzd össze: req.params.splat.join("/").

A splat név nem kötelező, csak szokás — /*utvonal esetén a kulcs utvonal lesz.

Az opcionális szakasz ({…})

app.get("/users{/:id}", (req, res) => {
  res.json({ params: req.params });
});
GET /users      →   {"params":{}}          — a req.params.id itt undefined
GET /users/7    →   {"params":{"id":"7"}}

Figyeld meg, hogy a perjel is a kapcsos zárójelen belül van: {/:id}, nem /{:id}. Így a /users (perjel nélkül) is illeszkedik.

KELL EZ NEKÜNK?

A gyakorlatban ritkán. Sokkal olvashatóbb két külön útvonal: app.get("/users", …) a listához és app.get("/users/:id", …) az egyeshez — más is a válaszuk (tömb vs. objektum). Az opcionális szakaszt akkor érdemes bevetni, ha a két eset tényleg ugyanaz a művelet.

Ami továbbra is működik

app.get(["/api/products", "/api/termekek"], …);   // ✅ több minta tömbben
app.get(/^\/x\d+$/, …);                            // ✅ reguláris kifejezés

Reguláris kifejezést csak akkor használj, ha tényleg nincs más — nehezen olvasható, és a paraméterek nevesítése is körülményes.

Kis- és nagybetű, záró perjel

Az Express alapból elnéző:

KérésRegisztrált útvonalIlleszkedik?
GET /termekapp.get("/Termek", …)✅ igen — nem érzékeny a kis- és nagybetűre
GET /valami/app.get("/valami", …)✅ igen — a záró perjel nem számít

Mindkét viselkedés átállítható (app.set("case sensitive routing", true), app.set("strict routing", true)), de az alapbeállítás a barátságosabb — hagyd békén.

7express.Router() — végpontok külön fájlba

Eddig minden útvonal az app.js-ben volt. Egy valódi API-nál ez néhány száz sor után áttekinthetetlen — ráadásul minden útvonal elején ott az ismétlődő /api/products előtag.

A Router egy önálló, mini alkalmazás: ugyanúgy vesz fel útvonalakat, de nem indít szervert. Kész állapotban „felakasztjuk” az alkalmazásra egy előtag alá.

routes/products.jsRELATÍV
import express from "express";

const router = express.Router();

router.get("/", (req, res) => {
  res.json(termekek);
});

router.get("/:id", (req, res) => {
  res.json({ id: Number(req.params.id) });
});

router.post("/", (req, res) => {
  res.status(201).json({ letrehozva: true });
});

export default router;
app.jsAZ ELŐTAG
import express from "express";
import termekRouter from "./routes/products.js";

const app = express();
app.use(express.json());

// ide akasztjuk fel:
app.use("/api/products", termekRouter);

export default app;
AZ ELŐTAG — app.js "/api/products" A ROUTER — products.js router.get("/") router.get("/:id") router.post("/") relatív — előtag nélkül + A TÉNYLEGES VÉGPONTOK GET /api/products GET /api/products/:id POST /api/products a szürke rész az előtagból jön — egyetlen helyről Ha a routerben is kiírnád az előtagot, az eredmény /api/products/api/products lenne — ez a tipikus elgépelés.
4. ábra — Az előtag és a relatív útvonalak együtt adják ki a végpontot.
ElemMit csinál?
express.Router()Létrehoz egy üres, önálló útvonalgyűjteményt
router.get("/", …)A router saját gyökeréhez képest értelmezi az útvonalat
app.use(elotag, router)„Felakasztja” a routert: minden benne lévő útvonal elé kerül az előtag

Mit nyerünk vele?

ElőnyMagyarázat
Egy fájl, egy erőforrásA routes/products.js csak a termékekkel foglalkozik
Az előtag egy helyen vanHa /api/products-ból /api/v2/products lesz, egyetlen sort írsz át
Rövidebb minták"/" és "/:id" a hosszú teljes útvonalak helyett
ÚjrafelhasználhatóUgyanaz a router több előtag alá is felakasztható

Hova kerülnek a fájlok?

webshop-api/
  app.js                  ← az alkalmazás felépítése, a routerek felakasztása
  index.js                ← a szerver indítása
  routes/
    products.js           ← /api/products végpontjai
    categories.js         ← /api/categories végpontjai
    orders.js             ← /api/orders végpontjai

Ez a szerkezet a 7. fejezetben bővül tovább (controller- és service-réteggel) — most elég annyi, hogy a végpontok erőforrásonként külön fájlba kerülnek.

Mit tudunk meg a kérésből egy routerben?

router.get("/", (req, res) => {
  res.json({ baseUrl: req.baseUrl, url: req.url, originalUrl: req.originalUrl });
});

GET /api/orders esetén az eredmény:

{ "baseUrl": "/api/orders", "url": "/", "originalUrl": "/api/orders" }
TulajdonságMit tartalmaz?
req.baseUrlAz előtag, ami alá a routert akasztottuk: /api/orders
req.urlA routeren belüli útvonal: /
req.originalUrlA kliens által kért teljes útvonal: /api/orders

Naplózáshoz szinte mindig az originalUrl kell — az url a routerben félrevezető lenne.

Egymásba ágyazott routerek: mergeParams

Ha a routert paraméteres előtag alá akasztod, a paraméter alapból nem látszik a belső routerben. Ezért kell a mergeParams:

const tetelekRouter = express.Router({ mergeParams: true });

tetelekRouter.get("/", (req, res) => {
  res.json(req.params);        // { shopId: "12" }
});

const boltRouter = express.Router();
boltRouter.use("/:shopId/items", tetelekRouter);

app.use("/shops", boltRouter);
GET /shops/12/items   →   {"shopId":"12"}

mergeParams: true nélkül a req.params üres objektum lenne. Ez ritkábban kell, de amikor igen, órákat lehet vele elveszíteni.

8Kényelmi eszközök

app.route() — ugyanaz az útvonal, több metódus

app.route("/api/products/:id")
  .get((req, res) => res.json({ muvelet: "lekérés" }))
  .put((req, res) => res.json({ muvelet: "felülírás" }))
  .delete((req, res) => res.sendStatus(204));

Az útvonalminta egyszer szerepel — kevesebb az elgépelés esélye. A működés azonos azzal, mintha három külön app.get / app.put / app.delete hívást írnál.

Amit nem regisztráltál, arra 404 jön: a fenti kódnál a PATCH /api/products/42 kérésre 404-et kapsz.

app.all() — minden metódus

app.all("/mind", (req, res) => {
  res.json({ metodus: req.method });
});
GET /mind      →   {"metodus":"GET"}
POST /mind     →   {"metodus":"POST"}
DELETE /mind   →   {"metodus":"DELETE"}

Ritkán kell — jellemzően közös előfeldolgozásra (naplózás, hitelesítés-ellenőrzés) egy adott útvonalon. Erre viszont a 6. fejezetben tanult middleware általában jobb eszköz.

Amit az Express magától elintéz

Az 1. blokk 2. fejezetében volt szó a HEAD és az OPTIONS metódusról. Az Express ezeket automatikusan kezeli a regisztrált útvonalaid alapján:

OPTIONS /api/lanc   →   200,  Allow: GET, HEAD, POST

Ez is kipróbált eredmény: a .get() és .post() regisztrációból az Express kitalálta, mit írjon az Allow fejlécbe — és a HEAD-et magától hozzátette a GET mellé.

Több kezelőfüggvény egy útvonalon

Egy útvonalhoz több függvényt is megadhatsz, vesszővel elválasztva:

function bejelentkezveKell(req, res, next) {
  // …ellenőrzés…
  next();                       // ← továbbadjuk a következőnek
}

app.delete("/api/products/:id", bejelentkezveKell, (req, res) => {
  res.sendStatus(204);
});

A next() hívás adja tovább a vezérlést a láncban következő függvénynek. Ez már middleware — a 6. fejezet erről szól részletesen. Itt csak annyit jegyezz meg, hogy az útvonal kezelőfüggvénye nem feltétlenül egyetlen függvény.

9Gyakori hibák

A végpont „nem létezik”, pedig megírtad

app.get("/api/products/:id", …);
app.get("/api/products/uj", …);   // ❌ soha nem fut le

Tünet: a /api/products/uj a :id ágra fut, id: "uj" értékkel.
Megoldás: a konkrét minta a paraméteres elé.

Az összehasonlítás mindig hamis

const termek = termekek.find(t => t.id === req.params.id);   // ❌ "42" === 42

Tünet: mindig 404, pedig a termék létezik.
Megoldás: Number(req.params.id).

A régi joker-szintaxis

app.get("*", (req, res) => res.status(404).json({ error: "Nincs ilyen végpont." }));

Tünet: a szerver el sem indul:

PathError [TypeError]: Missing parameter name at index 1: *; visit https://git.new/pathToRegexpError for info

Megoldás: mintát meg sem adunk, csak app.use()-t használunk a lánc végén:

app.use((req, res) => {
  res.status(404).json({ error: { message: "Nincs ilyen végpont." } });
});

Erről a 8. fejezetben (hibakezelés) lesz szó részletesen — ott az is kiderül, miért a legvégén.

Az előtag kétszer szerepel

// app.js
app.use("/api/products", termekRouter);

// routes/products.js
router.get("/api/products", …);        // ❌

Tünet: a végpont csak a /api/products/api/products címen érhető el.
Megoldás: a routerben relatív útvonal: router.get("/", ...).

Elmarad a return a korai válasz után

if (!termek) {
  res.status(404).json({ error: "Nincs ilyen termék." });   // ❌ nincs return
}
res.json(termek);                                            // lefut ez is

Tünet: ERR_HTTP_HEADERS_SENT a terminálban. A 2. fejezetben már láttuk — Expressben is ugyanez a szabály: egy kérésre egy válasz.

10Feladatok

Elméleti

F1⭐Melyik fut le?

Adott ez a sorrend:

app.get("/api/products/:id", A);
app.get("/api/products/akcios", B);
app.get("/api/products", C);

Melyik függvény fut le a GET /api/products/akcios kérésre? Miért? Hogyan javítanád?

F2⭐Params vagy query?

Döntsd el mindegyikről, útvonalparaméter vagy query paraméter legyen, és indokold:

  • a) egy termék azonosítója
  • b) a lista hányadik oldalát kérjük
  • c) melyik kategória termékeit kérjük
  • d) egy rendelés azonosítója
  • e) rendezés ár szerint
F3⭐⭐Hibakeresés

Miért nem talál soha semmit?

app.get("/api/products/:id", (req, res) => {
  const termek = termekek.find(t => t.id === req.params.id);
  if (!termek) return res.status(404).json({ error: "Nincs ilyen." });
  res.json(termek);
});
F4⭐⭐Fordítsd le Express 5-re

A következő négy sor mind hibát dob. Írd át mindegyiket működő alakra, és írd le, mit jelentett eredetileg:

app.get("*", …);
app.get("/kepek/*", …);
app.get("/users/:id?", …);
app.get("/:id(\d+)", …);
F5⭐⭐⭐Tervezz végponttérképet

Írd le egy blog API végpontjait (bejegyzések és hozzászólások), metódussal és útvonalmintával együtt. A hozzászólások egy bejegyzéshez tartoznak — ezt is fejezze ki az útvonal! Jelöld meg, melyik végponthoz kellene mergeParams.

Gyakorlati

G1⭐Végpontok metódusonként

Készíts egy app.js-t, amiben mind a hat termék-végpont szerepel (az 1. szakasz listája szerint), és mindegyik egy rövid JSON-nal válaszol, amiből kiderül, melyik futott le. Próbáld ki mindet REST Clienttel!

G2⭐⭐Azonosító helyesen

Készítsd el a GET /api/products/:id végpontot egy memóriában tárolt tömbbel. Kezelje helyesen:

  • létező azonosító → 200 + a termék
  • nem létező azonosító → 404 + hibaüzenet
  • nem szám azonosító (/api/products/alma) → 400 + hibaüzenet
G3⭐⭐Szűrés és lapozás

Bővítsd a GET /api/products végpontot:

  • ?categoryId=3 → csak az adott kategória termékei
  • ?q=egér → névre szűrés (kis- és nagybetű nélkül)
  • ?page=2&limit=5 → lapozás, alapértékekkel

A válasz tartalmazza a találatok összes számát is, ne csak az aktuális oldalt!

G4⭐⭐Routerek

Szervezd át a G1–G3 megoldásodat: hozz létre egy routes/products.js fájlt express.Router()-rel, és akaszd fel az app.js-ben a /api/products előtag alá. Ellenőrizd, hogy minden végpont ugyanúgy működik, mint előtte!

G5⭐⭐⭐Beágyazott erőforrás

Készíts egy routes/categories.js routert, benne:

  • GET /api/categories → kategóriák listája
  • GET /api/categories/:id → egy kategória
  • GET /api/categories/:id/products → az adott kategória termékei

A harmadik végpontot oldd meg külön routerrel, mergeParams: true beállítással!

11Önellenőrző kvíz

0 / 10 megválaszolva

12Összegzés

  1. Egy útvonal metódusból, útvonalmintából és kezelőfüggvényből áll. Ugyanaz a minta más metódussal más végpont.
  2. Az Express fentről lefelé keresi az első illeszkedő útvonalat, és megáll az elsőnél — ezért a konkrét minta mindig a paraméteres elé kerül.
  3. Az útvonalparaméter (:id) azt mondja meg, melyik erőforrásról van szó; a query paraméter (?page=2) azt, hogyan kérjük.
  4. Mindkettő szövegként érkezik. A req.params.id és a req.query.page értéke "42" és "2" — ha számként kell, Number()-rel alakítjuk át, és ellenőrizzük az eredményt.
  5. A req.query értéke lehet tömb (ismételt kulcs) és üres szöveg (érték nélküli kulcs) is — mindkettőre fel kell készülni.
  6. Az Express 5 mintaszintaxisa megváltozott: * helyett /*splat, :id? helyett {/:id}, a mintába írt reguláris kifejezés pedig megszűnt. A hibák indításkor derülnek ki.
  7. Az express.Router() erőforrásonként külön fájlba szervezi a végpontokat; az előtag az app.use() hívásban egyetlen helyen szerepel.
  8. Az app.route() a metódusok láncolására, az app.all() a minden metódusra való reagálásra való — az utóbbira általában a middleware a jobb megoldás.

13Fogalomtár

FogalomJelentés
Útvonal (route)Metódus + útvonalminta + kezelőfüggvény hármasa
ÚtvonalmintaA cím mintája, pl. /api/products/:id
ÚtvonalparaméterA mintában :név alakban jelölt, változó szakasz
req.paramsAz útvonalparaméterek objektuma — értékei mindig szövegek
Query paraméterA ? utáni rész: szűrés, rendezés, lapozás
req.queryA query paraméterek objektuma — szöveg vagy szövegtömb értékekkel
Joker / splatTöbb szakaszra illeszkedő minta; Express 5-ben nevet kell adni neki: /*splat
express.Router()Önálló útvonalgyűjtemény, ami előtag alá akasztható
req.baseUrlAz az előtag, ami alá a routert akasztottuk
req.originalUrlA kliens által kért teljes útvonal
mergeParamsRouter-beállítás: átveszi a szülő router paramétereit is
app.route()Egy útvonalminta, több metódus — láncolva
← Előző 3. Express bevezetés