Lekce 4 - Kompletní RESTful API v Node.js
V minulé lekci, Rozběhnutí projektu a první řádky v Expressu, jsme si připravili pracovní prostředí a začali jsme používat knihovnu Express.
V následujícím Node.js tutoriálu implementujeme kompletní RESTful API v Node.js pro databázi filmů. Cestou se naučíme používat několik užitečných nástrojů jako jsou nodemon a Postman.
Pro získání přehledu doporučujeme nyní zhlédnout lekci Dokumentace k Node.js API, kde je kompletně popsané API, které nyní budeme několik lekcí vytvářet. Na konci lekce je pak odkaz, který vede zpět sem.
Teď už se ale pustíme do instalace nástroje Postman.
Postman
Postman je software pro testování a volání různých API, včetně našeho RESTful. Umožňuje posílat, testovat a dokumentovat HTTP požadavky a odpovědi. Ke stažení je volně k dispozici na Download Postman. Po stažení spustíme klasický instalátor a aplikaci nainstalujeme. Za chvíli ji využijeme 🙂
Založení projektu
Založíme si nový adresář movie-api/
a otevřeme v
něm nové okno příkazové řádky z průzkumníku Windows pomocí
klávesy Shift a pravého tlačítka myši. Dále budeme používat
balíčkovací systém npm pro jehož inicializaci potřebujeme soubor
package.json
. Pro jeho vytvoření zadáme do příkazové
řádky:
npm init --yes
Ihned poté provedeme instalaci frameworku Express. Z příkazové řádky spustíme:
npm install express@~4.18.1
Prostředí máme připravené. V projektu poté vytvoříme ještě soubor
index.js
, do kterého napíšeme následující kód:
const express = require('express'); const app = express(); const port = 3000; app.listen(port, () => console.log("Listening on port " + port + "..."));
Nodemon
Dosud jsme při každé úpravě kódu museli naši aplikaci restartovat.
Nástroj nodemon nabízí lepší řešení. Nodemon je zkratka za node monitor.
Hlídá všechny soubory typu .js
(a pár dalších typů) v dané
složce a podsložkách. Kdykoli se některý z těchto souborů změní,
nodemon aplikaci sám restartuje. Veškeré změny tak můžeme rovnou
vyzkoušet.
Instalace nodemon
Nodemon nainstalujeme opět pomocí npm. Nainstalujeme jej globálně, což
provedeme s použitím parametru -g
:
npm install -g nodemon
Nyní aplikaci spustíme. Pro spuštění aplikace zadáme do příkazové řádky:
nodemon index.js
Metoda GET
Metodu GET
jsme používali již minule. Nově bude vracet
všechny filmy nebo detail jednoho konkrétního filmu. K tomu budeme
samozřejmě nejdříve potřebovat databázi filmů.
Příprava dat
Pro začátek nebudeme ještě používat databázi (tu si přidáme v některé z příštích lekcí), ale budeme filmy držet v poli:
const movies = [ { id: 1, name: "Kill Bill", year: 2003 }, { id: 2, name: "Kill Bill 2", year: 2004 }, { id: 3, name: "Star Wars IV", year: 1976 }, { id: 4, name: "Star Wars V", year: 1980 }, ];
O filmech bychom mohli uchovávat mnohem více dat (a v databázi také budeme), zatím nám však postačí id filmu, jeho jméno a rok premiéry.
Implementace metody GET
není náročná. Přehled cest jsme si
udělali již dříve a metodu GET
už umíme:
app.get('/api/movies', (req, res) => { res.send(movies); }); app.get('/api/movies/:id', (req, res) => { const id = Number(req.params.id); const movie = movies.find(movie => movie.id === id); if (movie) { res.send(movie); } else { res.status(404).send('Film nebyl nalezen.'); } });
V prvním volání metody app.get()
bez parametru se ptáme na
všechny filmy, vrátí se tedy celé pole. Při druhém volání požíváme
parametr id
, pomocí metody find()
na vyhledávání v
poli najdeme správný film a vrátíme ho. Pokud by film neexistoval, vrátíme
chybovou hlášku a HTTP kód 404
s hláškou
Film nebyl nalezen.
.
Nyní již aplikaci nemusíte restartovat, stačí kód uložit. Po zadání
http://localhost:3000/api/movies
do adresního řádku se zobrazí
seznam filmů:
Když přidáme ještě jedno lomítko a id filmu, zobrazí se jen data o konkrétním filmu:
Pokud zadáme id, pro které film neexistuje, zobrazí se chybová hláška.
Ve vývojářských nástrojích pod klávesou F12 se v panelu
Network přesvědčíme, že nám server vrátil kód
404
:
Metoda POST
Určitě jste si všimli, že se pole s filmy v prohlížeči nezobrazilo moc
hezky. Prohlížeč je totiž určený pro vykreslování webových stránek, ne
pro zobrazování polí. Požadavek s metodou POST
bychom dokonce
měli problém i odeslat. Proto přichází na scénu Postman. Program
spustíme:
Po spuštění zvolíme metodu GET (1), do adresního řádku napíšeme totéž, co do adresního řádku prohlížeče (2), a klikneme na tlačítko Send (3).
Zobrazí se nám seznam filmů nebo konkrétní film, podle toho, s jakou
cestou jsme požadavek poslali. Kromě toho nám Postman ukazuje HTTP kód
200 OK
, se kterým přišla odpověď (4) a mnoho dalších
informací.
Můžeme si ještě ověřit, že vrátí kód 404
při zadání
GET
požadavku na neexistující id filmu. Stejně jako v
prohlížeči si v něm můžeme vytvářet záložky na různé požadavky
pomocí tlačítka + (5).
Od teď už budeme všechny požadavky na naše API posílat pomocí Postmana.
Implementace POST
Když máme Postmana, můžeme si naimplementovat metodu POST
.
Do index.js
vložme následující kód:
app.post('/api/movies', (req, res) => { const movie = { id: movies.length + 1, name: req.body.name, year: req.body.year }; movies.push(movie); res.send(movie); });
Po přidání kódu výše stačí soubor index.js
pouze uložit. Aplikaci již nemusíme restartovat.
Nyní voláme metodu app.post()
. Požadavek posíláme na cestu
bez parametru, protože id
nový film ještě nemá přiřazené.
Nyní ho tvoříme jako délku pole plus jedna. To sice nemusí být vždycky
správně, ale vzhledem k tomu, že časem to za nás bude dělat databáze, tak
nám to protentokrát bude stačit. Pak už jen přidáme film do databáze a
také ho zobrazíme jako odpověď. V odpovědi se film zobrazí včetně nově
vytvořeného id.
Aby kód fungoval, musíme na začátek souboru ihned po definici konstanty
port
přidat následující řádek:
app.use(express.json());
Teď si blíže vysvětlíme jeho funkci. Voláme jím takzvané middleware,
což jsou procesy, které se spouští mezi přijetím požadavku a odesláním
odpovědi (jsou uprostřed, proto middleware). Konkrétně
express.json()
parsuje tělo požadavku. Pokud v něm najde
nějaký JSON, naplní s ním hodnotu vlastnosti req.body
. Bez
tohoto middleware bychom v req.body
nic nenašli.
Nyní již můžeme přejít do Postmana a na nové záložce (1) založit nový požadavek. Metoda bude POST (2), vyplníme adresu, nastavíme tělo požadavku (3), vybereme raw (4) a typ JSON (5).
Do těla požadavku vyplníme šestý díl Star Wars, který nám v původním seznamu chyběl:
{ "name": "Star Wars VI", "year": 1983 }
Vše musí být přesně podle formátu JSON, tedy i jména vlastností musí být v uvozovkách. Požadavek odešleme kliknutím na tlačítko Send:
Validace dat
Zatím jsme data do aplikace posílali sami. Je tedy samozřejmé, že si do aplikace nebudeme úmyslně posílat špatná data. Může se nám ale snadno stát, že uděláme omylem nějaký překlep. A co když nebudeme data do aplikace posílat my, ale někdo jiný?
Je zásadně vhodné vždy pečlivě ověřit, jestli data, která nám někdo posílá, jsou v pořádku a odpovídají tomu, co očekáváme.
Rychlou validaci si můžeme napsat sami. U naší filmové databáze by stačilo ověřit, že jméno filmu je řetězec a rok premiéry je číslo. Ale opět - časem budeme potřebovat komplexnější validaci, a proč psát spoustu kódu navíc, když na ni už existují hotové balíčky? Jeden z nich, velmi oblíbený, se jmenuje Joi.
Instalace Joi
Pro instalaci nástroje Joi opět použijeme balíčkovací systém npm. Z příkazové řádky spustíme:
npm install joi@~17.6.0
Pomocí @~17.6.0
za názvem knihovny jsme npm
řekli, že chceme verzi, kde je major verze 17
a minor
verze 6
. Verze tedy odpovídá formátu
17.6.x
. Vyhneme se tak nefunkčnosti ukázek v dalších verzích
knihovny, kde může autor zvolit jinou syntaxi.
Implementace Joi
Na začátek souboru přidáme následující kód:
const Joi = require('joi');
Při použití require()
se nám vrací třída, proto velké
J
. Na konec souboru poté přidáme funkce
validateMovie()
:
function validateMovie(movie) { const schema = Joi.object({ name: Joi.string().min(3).required(), year: Joi.number() }); return schema.validate(movie); }
Ve funkci validateMovie()
definujeme schéma, které říká,
že jméno bude řetězec o minimální délce tří znaků a je povinné. Rok
musí být číslo a povinný není. Potom pomocí metody
schema.validate()
porovnáme JSON z těla požadavku (parametr
movie
) s uvedeným schématem.
Metodu app.post()
poté upravíme tímto způsobem:
app.post('/api/movies', (req, res) => { const { error } = validateMovie(req.body); if (error) { res.status(400).send(error.details[0].message); } else { const movie = { id: movies.length + 1, name: req.body.name, year: req.body.year }; movies.push(movie); res.send(movie); } });
Pokud požadavek neodpovídá schématu, vrací se nám objekt s vlastností
error
. V tomto případě vrátíme uživateli chybovou hlášku,
kterou nám připravil Joi
, spolu s HTTP kódem
400 Bad Request
. Pokud je vše v pořádku, vlastnost
error
v objektu neexistuje a zbylý kód proběhne stejně jako
předtím.
Nyní je na čase poslat z Postmana několik požadavků, s validními ale i nevalidními daty, a vyzkoušet chování aplikace.
Metoda PUT
Filmovému znalci neunikne, že máme v kódu špatný rok u čtvrtého dílu
Star Wars. Pojďme ho tedy opravit - naučíme naše API obsluhovat metodu
PUT
.
Do index.js
vložíme následující kód a soubor
uložíme:
app.put('/api/movies/:id', (req, res) => { const id = Number(req.params.id); const movie = movies.find(movie => movie.id === id); if (!movie) { res.status(404).send('Film nebyl nalezen.'); return; } const { error } = validateMovie(req.body); if (error) { res.status(400).send(error.details[0].message); } else { movie.name = req.body.name; movie.year = req.body.year; res.send(movie); } });
Opakují se tu věci, které jsme již použili v jiných metodách, takže není třeba kód znovu vysvětlovat.
Přejdeme opět do Postmana, na nové záložce vybereme metodu PUT,
pošleme ji na koncový bod http://localhost:3000/api/movies/3
,
vybereme možnost raw, typ JSON a do těla vložíme:
{ "name": "Star Wars IV", "year": 1977 }
A klikneme na tlačítko Send. Poté pomocí GET požadavku ověříme, že film již má správně nastavený rok premiéry.
Metoda DELETE
Zbývá nám už jen metoda DELETE
, naštěstí velmi
jednoduchá. Na konec souboru přidáme kód:
app.delete('/api/movies/:id', (req, res) => { const id = Number(req.params.id); const movie = movies.find(movie => movie.id === id); if (!movie) { res.status(404).send('Film nebyl nalezen.'); } else { const index = movies.indexOf(movie); movies.splice(index, 1); res.send(movie); } });
Pomocí Postmana si vyzkoušíme poslat DELETE požadavek, abychom otestovali, že to opravdu funguje
To by bylo pro tuto lekci vše.
V další lekci, Úvod do MongoDB, si nainstalujeme databázi MongoDB a zkusíme se k ní také připojit.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 376x (3.39 kB)
Aplikace je včetně zdrojových kódů v jazyce JavaScript