Express alapok · 9. tananyag · a blokk záró fejezete
Itt áll össze egyetlen működő alkalmazássá minden, amit eddig tanultunk. Nem lesz benne új Express-fogalom — helyette végigcsináljuk a teljes utat a végponttérképtől a kipróbálásig, adatbázis nélkül, memóriában.
PUT és a
PATCH műveletet — kódban is;Mielőtt egy sort is írnánk, rögzítjük, mit ígér az API. Ez a „szerződés”, amiről az 1. blokk 2. fejezetében volt szó.
| Metódus | Útvonal | Mit csinál? | Siker | Hiba |
|---|---|---|---|---|
GET | /api/products | Terméklista szűréssel és lapozással | 200 | — |
GET | /api/products/:id | Egy termék | 200 | 400, 404 |
POST | /api/products | Új termék | 201 + Location | 400 |
PUT | /api/products/:id | Teljes felülírás | 200 | 400, 404 |
PATCH | /api/products/:id | Részleges módosítás | 200 | 400, 404 |
DELETE | /api/products/:id | Törlés | 204 | 400, 404 |
{ "id": 1, "nev": "Billentyűzet", "ar": 12990, "keszlet": 8, "categoryId": 3 }
| Mező | Típus | Kötelező? | Szabály |
|---|---|---|---|
id | szám | — | A szerver adja, a kliens soha nem küldi |
nev | szöveg | ✅ igen | Nem lehet üres; a szóközöket levágjuk |
ar | szám | ✅ igen | Nem lehet negatív |
keszlet | egész szám | nem | Nem lehet negatív; alapértéke 0 |
categoryId | egész szám | nem | Pozitív; alapértéke null |
| Paraméter | Példa | Alapérték | Mit csinál? |
|---|---|---|---|
categoryId | ?categoryId=3 | — | Szűrés kategóriára |
q | ?q=egér | — | Keresés a névben, kis- és nagybetűtől függetlenül |
page | ?page=2 | 1 | Hányadik oldal |
limit | ?limit=10 | 20 | Hány elem egy oldalon |
Lista — az adat mellé a lapozáshoz szükséges információ is kell:
{ "elemek": [ … ], "osszesTalalat": 3, "oldal": 1, "meret": 20 }
Hiba — mindig ugyanaz a szerkezet (5. és 8. fejezet):
{ "error": { "message": "Nincs ilyen termék.", "code": "TERMEK_NEM_TALALHATO" } }
Mert a lapozáshoz a kliensnek tudnia kell, hány találat van összesen — enélkül nem tud
lapozósávot rajzolni. Két bevett megoldás van: a becsomagolt objektum (ezt választjuk), vagy az
X-Total-Count fejléc. Mi mindkettőt megadjuk, mert a fejléc
olcsó, és van kliens, amelyik azt várja.
Nyolc helyzet a webshopból. Válaszd ki, melyik kérés tartozik hozzá — a metódust és az útvonalat együtt.
mkdir webshop-api cd webshop-api npm init -y npm install express
Egészítsd ki a package.json fájlt:
{
"type": "module",
"scripts": {
"dev": "node --watch --env-file=.env index.js",
"start": "node index.js"
}
}
A mappaszerkezet a 7. fejezetből:
webshop-api/
index.js
src/
app.js
routes/termek.routes.js
controllers/termek.controller.js
services/termek.service.js
middlewares/
ervenyes-azonosito.js
nem-talalhato.js
hibakezelo.js
utils/
HttpHiba.js
validalas.js
keresek.http
A service a memóriában tárolt tömböt kezeli. Nincs benne req,
res, se státuszkód — ez a 7. fejezet szabálya.
// src/services/termek.service.js let termekek = [ { id: 1, nev: "Billentyűzet", ar: 12990, keszlet: 8, categoryId: 3 }, { id: 2, nev: "Egér", ar: 4990, keszlet: 25, categoryId: 3 }, { id: 3, nev: "Monitor", ar: 89990, keszlet: 0, categoryId: 1 } ]; let kovetkezoId = 4; export function osszes({ categoryId, q, oldal = 1, meret = 20 } = {}) { let talalatok = termekek; if (categoryId !== undefined) { talalatok = talalatok.filter(t => t.categoryId === categoryId); } if (q) { const kis = q.toLowerCase(); talalatok = talalatok.filter(t => t.nev.toLowerCase().includes(kis)); } const osszesTalalat = talalatok.length; // a SZŰRT találatok száma — lapozás ELŐTT const kezdet = (oldal - 1) * meret; return { elemek: talalatok.slice(kezdet, kezdet + meret), osszesTalalat }; } export function azonositoval(id) { return termekek.find(t => t.id === id) ?? null; } export function letrehoz(adat) { const termek = { id: kovetkezoId++, nev: adat.nev, ar: adat.ar, keszlet: adat.keszlet ?? 0, categoryId: adat.categoryId ?? null }; termekek.push(termek); return termek; } export function felulir(id, adat) { const index = termekek.findIndex(t => t.id === id); if (index === -1) return null; termekek[index] = { // TELJES csere — a hiányzó mezők alapértékre állnak id, nev: adat.nev, ar: adat.ar, keszlet: adat.keszlet ?? 0, categoryId: adat.categoryId ?? null }; return termekek[index]; } export function modosit(id, valtozasok) { const termek = azonositoval(id); if (!termek) return null; Object.assign(termek, valtozasok); // csak a megadott mezőket írja felül return termek; } export function torol(id) { const index = termekek.findIndex(t => t.id === id); if (index === -1) return false; termekek.splice(index, 1); return true; }
| Függvény | Mit ad vissza, ha nincs találat? | Miért így? |
|---|---|---|
azonositoval | null | „Megkerestem, nincs ilyen” |
felulir, modosit | null | Ugyanaz — a vezérlő csinál belőle 404-et |
torol | false | Itt nincs mit visszaadni, csak a siker ténye |
let termekek ÉS AZ id KIOSZTÁSA IDEIGLENES
Adatbázisnál a kovetkezoId számláló eltűnik: az azonosítót maga az adatbázis
adja. A filter-ekből WHERE feltétel lesz, a
slice-ból pedig LIMIT/OFFSET.
A függvények neve és a visszaadott érték viszont változatlan marad — ezért nem kell
hozzányúlni a vezérlőhöz.
A bejövő adatot egy helyen ellenőrizzük, és HttpHiba-t dobunk
(8. fejezet):
// src/utils/validalas.js import { HttpHiba } from "./HttpHiba.js"; export function termekAdatok(torzs, { reszleges = false } = {}) { if (typeof torzs !== "object" || torzs === null || Array.isArray(torzs)) { throw new HttpHiba(400, "A kérés törzse hiányzik vagy nem objektum.", "HIANYZO_TORZS"); } const eredmeny = {}; // nev — kötelező, kivéve részleges módosításnál if (torzs.nev !== undefined) { if (typeof torzs.nev !== "string" || torzs.nev.trim() === "") { throw new HttpHiba(400, "A név nem lehet üres.", "ERVENYTELEN_NEV"); } eredmeny.nev = torzs.nev.trim(); } else if (!reszleges) { throw new HttpHiba(400, "A név megadása kötelező.", "HIANYZO_MEZO"); } // ar — kötelező, kivéve részleges módosításnál if (torzs.ar !== undefined) { if (typeof torzs.ar !== "number" || !Number.isFinite(torzs.ar) || torzs.ar < 0) { throw new HttpHiba(400, "Az ár csak nem negatív szám lehet.", "ERVENYTELEN_AR"); } eredmeny.ar = torzs.ar; } else if (!reszleges) { throw new HttpHiba(400, "Az ár megadása kötelező.", "HIANYZO_MEZO"); } // keszlet — mindig elhagyható if (torzs.keszlet !== undefined) { if (!Number.isInteger(torzs.keszlet) || torzs.keszlet < 0) { throw new HttpHiba(400, "A készlet csak nem negatív egész szám lehet.", "ERVENYTELEN_KESZLET"); } eredmeny.keszlet = torzs.keszlet; } // categoryId — mindig elhagyható if (torzs.categoryId !== undefined) { if (!Number.isInteger(torzs.categoryId) || torzs.categoryId < 1) { throw new HttpHiba(400, "A kategória azonosítója csak pozitív egész szám lehet.", "ERVENYTELEN_KATEGORIA"); } eredmeny.categoryId = torzs.categoryId; } if (reszleges && Object.keys(eredmeny).length === 0) { throw new HttpHiba(400, "Legalább egy módosítandó mezőt meg kell adni.", "NINCS_MODOSITAS"); } return eredmeny; }
| Feladat | Hogyan? |
|---|---|
| Ismétlés elkerülése | Egy függvény szolgálja ki a POST, PUT és PATCH végpontot |
| Kötelező vs. elhagyható | A reszleges kapcsoló dönti el: PATCH-nél semmi nem kötelező |
| Tisztítás | A nev értékéről levágja a felesleges szóközöket (trim) |
| Szűrés | Csak az ismert mezőket adja vissza — az id vagy bármi más ismeretlen kimarad |
return eredmeny
Nem a nyers req.body-t adjuk tovább, hanem egy általunk
összeállított objektumot. Így hiába küld a kliens
{"nev":"X","ar":1,"id":9999,"admin":true} törzset, az
id és az admin mező be sem kerül
az adatba. Ez a legegyszerűbb védekezés az ellen, hogy valaki olyan mezőt írjon felül, amit nem szabadna.
Mert így látod, mit is csinál egy ilyen könyvtár. A 3. blokkban a Zod veszi át ezt a szerepet: ott ugyanez a szabályrendszer néhány sor lesz, és a hibaüzeneteket is megkapod hozzá. Előbb viszont érdemes kézzel megírni egyszer.
A négy /:id végpont mindegyikének ugyanaz az első dolga. A 6. fejezetből
tudjuk, hogy ez middleware-be való:
// src/middlewares/ervenyes-azonosito.js import { HttpHiba } from "../utils/HttpHiba.js"; export function ervenyesAzonosito(req, res, next) { const id = Number(req.params.id); if (!Number.isInteger(id) || id < 1) { return next(new HttpHiba(400, "Az azonosító csak pozitív egész szám lehet.", "ERVENYTELEN_ID")); } req.termekId = id; // a már átalakított érték a vezérlőnek next(); }
Az eredmények:
GET /api/products/alma → 400 {"error":{"message":"Az azonosító csak pozitív egész szám lehet.", …}}
GET /api/products/-3 → 400 ugyanaz
GET /api/products/1 → 200 a termék
A Number.isInteger egyszerre zárja ki a NaN, a
tizedes tört és a -3 esetét — a Number("alma")
ugyanis NaN, a Number.isInteger(NaN) pedig hamis.
// src/controllers/termek.controller.js import * as szolgaltatas from "../services/termek.service.js"; import { termekAdatok } from "../utils/validalas.js"; import { HttpHiba } from "../utils/HttpHiba.js"; export function listaz(req, res) { const oldal = Number(req.query.page) || 1; const meret = Number(req.query.limit) || 20; const categoryId = req.query.categoryId ? Number(req.query.categoryId) : undefined; const { elemek, osszesTalalat } = szolgaltatas.osszes({ categoryId, q: req.query.q, oldal, meret }); res.set("X-Total-Count", String(osszesTalalat)); res.json({ elemek, osszesTalalat, oldal, meret }); } export function egy(req, res) { const termek = szolgaltatas.azonositoval(req.termekId); if (!termek) throw new HttpHiba(404, "Nincs ilyen termék.", "TERMEK_NEM_TALALHATO"); res.json(termek); } export function letrehoz(req, res) { const adat = termekAdatok(req.body); const termek = szolgaltatas.letrehoz(adat); res.location(`/api/products/${termek.id}`).status(201).json(termek); } export function felulir(req, res) { const adat = termekAdatok(req.body); // minden mező kötelező const termek = szolgaltatas.felulir(req.termekId, adat); if (!termek) throw new HttpHiba(404, "Nincs ilyen termék.", "TERMEK_NEM_TALALHATO"); res.json(termek); } export function modosit(req, res) { const valtozasok = termekAdatok(req.body, { reszleges: true }); // csak amit küldtek const termek = szolgaltatas.modosit(req.termekId, valtozasok); if (!termek) throw new HttpHiba(404, "Nincs ilyen termék.", "TERMEK_NEM_TALALHATO"); res.json(termek); } export function torol(req, res) { const sikerult = szolgaltatas.torol(req.termekId); if (!sikerult) throw new HttpHiba(404, "Nincs ilyen termék.", "TERMEK_NEM_TALALHATO"); res.sendStatus(204); }
Figyeld meg, milyen rövidek. Ez azért lehetséges, mert:
| Amit nem kell megírni | Hol van? |
|---|---|
A return a korai válasz elé | throw — a függvény magától megszakad (8. fejezet) |
| A hibaválasz összerakása | A központi hibakezelőben (8. fejezet) |
| Az azonosító átalakítása és ellenőrzése | A middleware-ben (5. szakasz) |
| A törzs ellenőrzése | A termekAdatok függvényben (4. szakasz) |
| Az adatkezelés | A service-ben (3. szakasz) |
res.sendStatus(204) A TÖRLÉSNÉL
A 204 No Content azt jelenti: „sikerült, és nincs mit visszaadnom”.
Törzset nem küldünk — ezért nem res.json()-t használunk
(3. fejezet).
PUT vs. PATCHEz a fejezet legfontosabb REST-fogalma, és a leggyakoribb félreértés.
PUT alapértékre állítja azt, amit nem küldtél.PUT | PATCH | |
|---|---|---|
| Mit jelent? | „Így nézzen ki az erőforrás” | „Ezt változtasd meg rajta” |
| A törzs | A teljes objektum | Csak a módosítandó mezők |
| Kötelező mezők | nev, ar | egyik sem, de legalább egy kell |
| A hiányzó mezők | alapértékre állnak | változatlanok maradnak |
| Kódban | termekAdatok(req.body) | termekAdatok(req.body, { reszleges: true }) |
| A service-ben | termekek[index] = { … } — csere | Object.assign(termek, …) — összefésülés |
PATCH-ET HASZNÁLJUK GYAKRABBAN
Egy űrlapon ritkán szerkeszted az összes mezőt egyszerre, és a PUT könnyen
letöröl olyan adatot, amihez hozzá sem nyúltál. A PUT ott jó, ahol tényleg a
teljes objektumot küldi a kliens.
A PUT idempotens: ugyanazt a kérést kétszer elküldve
ugyanaz az eredmény. A PATCH nem feltétlenül az — egy
„növeld eggyel a készletet” típusú módosítás kétszer futtatva mást ad.
Öt kérés, mindig ugyanabból a kiindulási termékből. Válaszd ki az eredményt!
app.js// src/routes/termek.routes.js import express from "express"; import * as vezerlo from "../controllers/termek.controller.js"; import { ervenyesAzonosito } from "../middlewares/ervenyes-azonosito.js"; const router = express.Router(); router.get("/", vezerlo.listaz); router.post("/", vezerlo.letrehoz); router.get("/:id", ervenyesAzonosito, vezerlo.egy); router.put("/:id", ervenyesAzonosito, vezerlo.felulir); router.patch("/:id", ervenyesAzonosito, vezerlo.modosit); router.delete("/:id", ervenyesAzonosito, vezerlo.torol); export default router;
router.route("/:id").all(ervenyesAzonosito) ?
Első ránézésre rövidebb lenne — de kipróbálva elrontja az OPTIONS
kezelését. Az .all() ugyanis az OPTIONS
kérésre is lefut, az Express pedig ilyenkor már nem generálja le magától az
Allow fejlécet:
.all()-lal: OPTIONS /api/products/1 → 404 metódusonként: OPTIONS /api/products/1 → 200, Allow: DELETE, GET, HEAD, PATCH, PUT
A metódusonkénti felsorolás négy karakterrel hosszabb, cserébe helyes marad az automatikus
OPTIONS- és HEAD-kezelés (4. fejezet).
cors() IS FUT A LÁNCBAN
A fenti eredmény az ebben a fejezetben épülő app.js-re vonatkozik. Ha
kiegészíted a 6. fejezet middleware-eivel, a cors()
maga válaszol az OPTIONS-kérésekre —
204-gyel, Allow fejléc nélkül —,
még mielőtt a router szóhoz jutna:
cors() nélkül: OPTIONS /api/products/1 → 200, Allow: DELETE, GET, HEAD, PATCH, PUT cors()-szal: OPTIONS /api/products/1 → 204, Allow fejléc nélkül
Ez nem hiba: a CORS-szabvány így írja elő az előkérés (preflight) kezelését — a
böngészőnek nem az Allow, hanem az
Access-Control-Allow-Methods fejléc kell, amit a
cors() küld. Az .all() csapdája viszont ettől
függetlenül valódi: cors() nélkül tényleg
404-et adna.
// src/app.js import express from "express"; import termekRouter from "./routes/termek.routes.js"; import { nemTalalhato } from "./middlewares/nem-talalhato.js"; import { hibakezelo } from "./middlewares/hibakezelo.js"; const app = express(); app.use(express.json({ limit: "100kb" })); // 1) törzs-feldolgozás app.use("/api/products", termekRouter); // 2) útvonalak app.use(nemTalalhato); // 3) 404 app.use(hibakezelo); // 4) hibakezelő — legvégül export default app;
A hibakezelő a törzs-feldolgozó hibáit is a saját formátumunkra fordítja (5. és 8. fejezet):
// src/middlewares/hibakezelo.js — az eleje export function hibakezelo(err, req, res, next) { if (err.type === "entity.parse.failed") { return res.status(400).json({ error: { message: "A kérés törzse nem érvényes JSON.", code: "ERVENYTELEN_JSON" } }); } if (err.type === "entity.too.large") { return res.status(413).json({ error: { message: "A kérés törzse túl nagy.", code: "TUL_NAGY_TORZS" } }); } // …a 8. fejezet szerinti általános rész… }
A keresek.http fájl (REST Client, 1. blokk 5. fejezet):
@alap = http://localhost:3000 ### Összes termék GET {{alap}}/api/products ### Szűrés kategóriára GET {{alap}}/api/products?categoryId=3 ### Új termék POST {{alap}}/api/products Content-Type: application/json { "nev": "Webkamera", "ar": 19990, "categoryId": 2 } ### Részleges módosítás PATCH {{alap}}/api/products/1 Content-Type: application/json { "ar": 22990 } ### Törlés DELETE {{alap}}/api/products/2
Kattints egy kérésre, és nézd meg a valódi választ. Minden itt látható eredmény a fenti kódból felépített API-tól származik — kipróbáltuk, nem kitaláltuk.
Válassz egy kérést a gombokkal.
—
—
A bal oldali gombokkal küldhetsz kérést. A jobb oldalon a státuszkód, a fontos fejlécek és a válasz törzse jelenik meg.
| Kérés | Státusz | Válasz |
|---|---|---|
GET /api/products | 200 | 3 elem, X-Total-Count: 3 |
GET /api/products?categoryId=3 | 200 | 2 elem, osszesTalalat: 2 |
GET /api/products?q=egÉR | 200 | 1 elem — a keresés nem érzékeny a kis- és nagybetűre |
GET /api/products?page=2&limit=2 | 200 | a 3. elem, osszesTalalat: 3 |
GET /api/products/1 | 200 | a termék |
POST /api/products | 201 | Location: /api/products/4 |
DELETE /api/products/2 | 204 | (üres törzs) |
OPTIONS /api/products/1 | 200 | Allow: DELETE, GET, HEAD, PATCH, PUT — cors() nélkül |
| Kérés | Státusz | code |
|---|---|---|
GET /api/products/alma | 400 | ERVENYTELEN_ID |
GET /api/products/-3 | 400 | ERVENYTELEN_ID |
GET /api/products/999 | 404 | TERMEK_NEM_TALALHATO |
POST a { "ar": 100 } törzzsel | 400 | HIANYZO_MEZO |
POST a { "nev": "X", "ar": "sok" } törzzsel | 400 | ERVENYTELEN_AR |
POST a { "nev": "X", "ar": -5 } törzzsel | 400 | ERVENYTELEN_AR |
POST a { …, "keszlet": 1.5 } törzzsel | 400 | ERVENYTELEN_KESZLET |
POST a {"nev": hibas} törzzsel | 400 | ERVENYTELEN_JSON |
PATCH üres ({}) törzzsel | 400 | NINCS_MODOSITAS |
PUT /api/products/999 | 404 | TERMEK_NEM_TALALHATO |
GET /nincs-ilyen | 404 | NINCS_VEGPONT |
POST ÜRES TÖRZSEL
Ha csak a Content-Type: application/json fejlécet küldöd, törzs nélkül, a
req.body értéke {} lesz (5. fejezet), és a
validáció HIANYZO_MEZO hibát ad. Ez helyes: a kérés tényleg hiányos volt.
Ez a lista nem hiányérzet, hanem a következő blokkok tartalomjegyzéke:
| Hiányzik | Miért baj? | Mikor jön? |
|---|---|---|
| Tartós tárolás | Újraindításkor minden adat elveszik | 3. blokk — Prisma + MySQL |
| Kapcsolatok | A categoryId csak egy szám; nincs mögötte valódi kategória | 3. blokk |
| Validációs könyvtár | A kézi validáció hosszú és könnyen hiányos | 3. blokk — Zod |
| Hitelesítés | Bárki törölhet bármit | 4. blokk — JWT |
| Jogosultság | Nincs különbség admin és vásárló között | 4. blokk |
| Tesztek | Minden ellenőrzés kézi | 4. blokk — node --test |
| Több egyidejű írás | Két párhuzamos POST ugyanazt az id-t kaphatná | 3. blokk — az adatbázis megoldja |
A végponttérkép, a státuszkódok, a hibaformátum, a rétegek és a validáció helye mind
változatlan lesz. A 3. blokkban egyetlen fájl, a termek.service.js belseje
cserélődik ki — és pontosan ezért érte meg rétegekre bontani.
id-t a kliens küldiconst termek = { ...req.body }; // ❌ a kliens id-je is bekerül
Tünet: a kliens felülírhat egy létező terméket, vagy ütköző azonosítót hoz létre.
Megoldás: a validáló függvény csak az ismert mezőket adja vissza, az
id-t a service adja.
PATCH letörli a mezőketexport function modosit(req, res) { const adat = termekAdatok(req.body); // ❌ hiányzik a { reszleges: true } }
Tünet: a PATCH ugyanúgy megköveteli a
nev és ar mezőt, mint a
PUT.
Megoldás: a reszleges kapcsoló átadása.
const elemek = talalatok.slice(kezdet, kezdet + meret); const osszesTalalat = elemek.length; // ❌ az OLDAL mérete, nem az összes találat
Tünet: a kliens azt hiszi, csak egy oldalnyi találat van.
Megoldás: az osszesTalalat értékét a lapozás
előtt, a szűrt listából vedd.
DELETE után is 200-at küldesz törzzselTünet: a kliens megpróbálja értelmezni a választ, pedig nincs mit.
Megoldás: res.sendStatus(204).
OPTIONS kérésre 404Ok: router.route("/:id") használata.
Megoldás: a middleware-t metódusonként add meg.
Ha 404 helyett 204-et kapsz
Allow fejléc nélkül, az nem ez a hiba: akkor a
cors() middleware válaszolt az előkérésre. Az a helyes viselkedés.
talalatok.filter(t => t.categoryId === req.query.categoryId); // ❌ "3" === 3 hamis
Megoldás: Number(req.query.categoryId) — a 4. fejezet
buktatója.
Elméleti
PUT vagy PATCH?Döntsd el mindegyikről, és indokold:
Melyik kód jár, és miért?
Mi a baj, és mi a tünete?
export function listaz(req, res) { const oldal = Number(req.query.page) || 1; const meret = Number(req.query.limit) || 20; const kezdet = (oldal - 1) * meret; const elemek = termekek.slice(kezdet, kezdet + meret); res.json({ elemek, osszesTalalat: elemek.length }); }
Miért nem adjuk tovább a nyers req.body-t a service-nek? Mondj két konkrét
támadási lehetőséget, amit a mezőszűrés megakadályoz!
Írd le a rendelések teljes szerződését: végponttérkép, a rendelés alakja, a kötelező és elhagyható mezők, a query paraméterek, a hibakódok. Gondolj arra is, hogy egy rendelés tételeket tartalmaz, és hogy egy leadott rendelést nem lehet akárhogy módosítani!
Gyakorlati
Építsd fel a projektet a 2. szakasz szerint, és készítsd el a két olvasó végpontot
(GET /api/products és GET /api/products/:id) a
rétegekkel együtt. Próbáld ki REST Clienttel!
Készítsd el a POST végpontot és a termekAdatok
függvényt. Bizonyítsd be mind az öt hibaesetet (hiányzó név, hiányzó ár, nem szám ár, negatív ár, tört
készlet), és jegyezd fel a státuszkódot és a hibakódot!
Egészítsd ki a PUT, PATCH és
DELETE végpontokkal. Bizonyítsd be a PUT és a
PATCH különbségét: a 7. szakasz példájával mutasd meg, hogy a
PUT alapértékre állítja a nem küldött keszlet
mezőt, a PATCH viszont nem!
Egészítsd ki a listavégpontot a categoryId, q,
page és limit paraméterekkel. Ellenőrizd, hogy
az osszesTalalat a szűrt találatok számát adja, nem az
oldalét!
Készítsd el ugyanígy a kategóriákat (/api/categories),
teljes CRUD-dal. Egészítsd ki egy GET /api/categories/:id/products
végponttal, amely az adott kategória termékeit adja vissza (4. fejezet:
mergeParams). Gondoskodj róla, hogy nem létező kategóriánál
404 legyen a válasz!
null-t vagy
false-t ad vissza, ha nincs találat, és nem ismeri a
HTTP-t.reszleges kapcsolóval
szolgálja ki a POST, PUT és
PATCH végpontot is, és csak az ismert mezőket engedi
tovább.PUT teljes cserét jelent (a hiányzó mezők alapértékre
állnak), a PATCH részleges módosítást (a nem küldött
mezők változatlanok). Mérve is megmutattuk.osszesTalalat a szűrt
találatok száma, a lapozás előtt számolva.router.route("/:id").all(…) rövidebbnek tűnik, de elrontja az
OPTIONS kezelését — a middleware-t metódusonként adjuk meg.| Fogalom | Jelentés |
|---|---|
| CRUD | Create · Read · Update · Delete — a négy alapművelet |
| Végponttérkép | Az API összes végpontja metódussal, útvonallal, státuszkódokkal |
| Szerződés | Amit az API ígér: mit fogad el és mit ad vissza |
PUT | Teljes felülírás; a hiányzó mezők alapértékre állnak |
PATCH | Részleges módosítás; a nem küldött mezők változatlanok |
| Validáció | A bejövő adat ellenőrzése és tisztítása |
| Mezőszűrés | Csak az ismert mezők átvétele — védekezés az id felülírása ellen |
| Lapozás | page és limit alapján az eredmény egy szeletének visszaadása |
X-Total-Count | Fejléc az összes találat számával |
204 No Content | Sikeres művelet, nincs visszaadandó tartalom |