ITnetwork summer 2020
80 % bodů zdarma na online výuku díky naší Letní akci!
Pouze tento týden sleva až 80 % na e-learning týkající se PHP

4. diel - Kompletné Restful API v Node.js

V minulej lekcii, Rozbehnutie projektu a prvé riadky v Expressu , sme si pripravili pracovné prostredie a začali sme používať knižnicu Express. Dnes v tutoriálu implementujeme kompletný Restful API v Node.js pre databázu filmov. Cestou sa naučíme používať niekoľko užitočných nástrojov.

Založenie projektu

Minule sme si pomocou NPM založili projekt a nainštalovali express. Pre dnešný aplikácii si vytvoríme nový projekt. Pre istotu si ešte raz uveďme príslušné príkazy:

mkdir node-projekt
cd node-projekt
npm init --yes
npm install express

V projekte potom ako minule do nového súboru index.js napíšeme nasledujúci kód:

const express = require('express');
const app = express();

app.listen(3000, () => console.log('Listening on port 3000...'));

Nodemon

Ak ste minule počas čítania článku písali aj kód, mohlo vás otravovať, že pri každej úprave kódu bolo treba aplikáciu reštartovať. Nástroj nodemon ponúka lepšie riešenie. Nodemon je skratka za node monitor. Stráži všetky súbory typu .js (a pár ďalších typov) v danej zložke a podpriečinkoch. Kedykoľvek sa niektorý z týchto súborov zmení, nodemon aplikáciu sám reštartuje. Všetky zmeny tak môžeme rovno vyskúšať.

Nodemon nainštalujeme opäť pomocou NPM. Väčšinou ho chceme nainštalovať globálne, s použitím parametra -g. Potom už len stačí zadať do príkazového riadku namiesto príkazu node index.js príkaz nodemon index.js:

npm install -g nodemon
nodemon index.js

Metóda GET

Metódu GET sme používali už minule. Novo bude vracať všetky filmy alebo detail jedného konkrétneho filmu. K tomu budeme samozrejme najskôr potrebovať databázu filmov.

Príprava dát

Pre začiatok nebudeme ešte používať databázu (tú si pridáme v niektorej z najbližších lekcií), ale budeme filmy držať 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 filmoch by sme mohli uchovávať oveľa viac dát (a v databáze tiež budeme), zatiaľ nám však postačí id, meno filmu a rok premiéry.

Implementácia metódy GET pre vás nebude ťažké, ak ste dobre čítali predchádzajúce lekciu. Prehľad ciest sme si urobili už v druhej lekcii a metódu GET už vieme:

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 prvom volanie metódy app.get() bez parametra sa pýtame na všetky filmy, vráti sa teda celé pole. Pri druhom volaní požívame parameter id, pomocou metódy find() na vyhľadávanie v poli nájdeme správny film a vrátime ho. Ak by film neexistoval, vrátime chybovú hlášku a HTTP kód 404 Not Found (požadovaný dokument nebol nájdený).

Teraz už aplikáciu nemusíte reštartovať, stačí kód uložiť. Po zadaní http://localhost:3000/api/movies do adresného riadku sa zobrazí zoznam filmov:

Your page
http://localhos­t:3000/api/mo­vies

Alebo môžete pridať ešte jedno lomítko a id filmu, zobrazí sa len dáta o konkrétnom filme.

Your page
http://localhos­t:3000/api/mo­vies/1

Skúste si zadať aj id, pre ktoré film neexistuje, zobrazí sa chybová hláška (a vo vývojárskych nástrojoch pod klávesom F12 sa v paneli "Network" môžete presvedčiť, že vám server vrátil kód 404).

Metóda POST

Určite ste si všimli, že sa pole s filmami v prehliadači nezobrazilo veľmi pekne. Prehliadač je totiž určený pre vykresľovanie webových stránok, nie pre zobrazovanie polí. Požiadavka s metódou POST by sme dokonca mali problém aj odoslať.

Postman

Preto sa hodí mať aplikáciu, ktorá nám umožní posielať všetky typy požiadaviek. Jednou takou je Postman. Stiahnite si ho z www.getpostman.com. Inštaláciu určite zvládnete sami.

Posielanie POST požiadavke na Node server v aplikácii Postman
Tento výukový obsah pomáhajú rozvíjať nasledujúce firmy, ktoré možno hľadajú práve teba!

Po spustení zvolíme metódu GET (1), do adresného riadku napíšeme to isté čo do adresného riadku prehliadača (2), a klikneme na tlačidlo "Send" (3).

Zobrazí sa nám zoznam filmov alebo konkrétny film, podľa toho, s akou cestou sme požiadavku poslali. Okrem toho nám Postman ukazuje HTTP kód 200 OK, s ktorým odpoveď prišla (4) a mnoho ďalších informácií.

Môžete si ešte vyskúšať, že vráti kód 404 pri zadaní GET požiadavke na neexistujúce id filmu. Rovnako ako v prehliadači si v ňom môžete vytvárať záložky na rôzne požiadavky pomocou tlačidla + (5).

Od teraz už budeme všetky požiadavky na naše API posielať pomocou Postman.

Implementácia POST

Keď máme Postman, môžeme si naimplementovať metódu POST. Do index.js vložme nasledujúci kód (a súbor uložme):

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);
});

Teraz voláme metódu app.post(). Požiadavka posielame na cestu bez parametra, pretože id nový film ešte nemá priradený. Teraz ho tvoríme ako dĺžku poľa plus jedna. (To nemusí byť vždy správne, ale vzhľadom k tomu, že časom to za nás bude robiť databázy, tak nám to tentokrát bude stačiť.) Potom už len pridáme film do databázy a tiež ho zobrazíme ako odpoveď (v odpovedi sa zobrazí vrátane novo vytvoreného id).

Aby kód fungoval, musíme na začiatok súboru (hneď po tom, čo definujeme konštantu app) pridať ešte tento riadok:

app.use(express.json());

Tým voláme takzvané middleware, čo sú procesy, ktoré sa spúšťa medzi prijatím požiadavky a odoslaním odpovede (sú uprostred, preto middleware). Konkrétne express.json() dekóduje telo požiadavke, a pokiaľ v ňom nájde nejaký JSON, tak s ním naplní hodnotu vlastnosti req.body. Bez tohto middleware by sme v req.body nič nenašli.

Teraz už môžeme prejsť do Postman a na novej záložke (1) založiť novú požiadavku. Metóda bude POST (2), vyplníme adresu, nastavíme telo požiadavke (3), vyberieme raw (4) a typ JSON (5).

Do tela požiadavke vyplníme šiesty diel Star Wars, ktorý nám v pôvodnom zozname chýbal. Nezabudnite, že všetko musí byť presne podľa formátu JSON, teda aj mená vlastností musí byť v úvodzovkách. A môžeme požiadavku poslať kliknutím na tlačidlo "Send".

Odoslanie požiadavky pomocou aplikácie Postman na Node.js API server

Validácia

Je to trochu odbočka, ale je to tak dôležité, že to nemožno nespomenúť:

Vždy overujte, či dáta, ktorá vám niekto posiela, sú v poriadku a zodpovedajú tomu, čo očakávate.

Zatiaľ si posielate do svojej aplikácie dáta sami, z Postman, takže si nebudete úmyselne posielať zlé dáta. Ale ste si istí, že tam nemáte žiadny preklep? A čo keď bude dáta posielať niekto cudzí?

Rýchlu validáciu si môžete napísať sami - stačilo by overiť, že meno filmu je reťazec a rok premiéry je číslo. Ale opäť - časom budeme potrebovať komplexnejšie validáciu, a prečo písať veľa kódu navyše, keď na ňu už existujú hotové balíky? Jeden z nich, veľmi obľúbený, sa volá Joi.

Nainštalujeme si ho pomocou NPM:

npm install joi

Na začiatok index.js pridajme nasledujúci kód:

const Joi = require('joi');

To, čo sa nám vracia pri použití require(), je trieda, preto veľké J.

Na koniec súboru pridajme funkciu validateMovie():

function validateMovie(movie) {
    const schema = {
        name: Joi.string().min(3).required(),
        year: Joi.number()
    };
    return Joi.validate(movie, schema);
}

Vo funkcii validateMovie() definujeme schému, ktoré hovorí, že meno bude reťazec o minimálnej dĺžke troch znakov a je povinné. Rok musí byť číslo a povinný nie je. Potom pomocou metódy Joi.validate() porovnáme JSON z tela požiadavke (parameter movie) s uvedeným schémou.

Metódu app.post() potom môžeme upraviť napríklad takto:

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);
    }
});

Ak požiadavka nezodpovedá schéme, vracia sa nám objekt s vlastnosťou error. V tomto prípade vrátime užívateľovi chybovú hlášku, ktorú nám pripravil Joi, spolu s HTTP kódom 400 Bad Request. Ak je všetko v poriadku, vlastnosť error v objekte neexistuje a zvyšný kód prebehne rovnako ako predtým.

Skúste si teraz poslať z Postman niekoľko POST požiadaviek s validnými alebo so zlými dátami a sledujte správanie aplikácie.

Metóda PUT

Ak ste filmoví znalci, iste vám neuniklo, že máme v kóde zlý rok u štvrtého dielu Star Wars. Poďme ho teda opraviť - naučíme naše API obsluhovať metódu PUT.

Do index.js vložme nasledujúci kód (a súbor nezabudnime uložiť):

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ú sa tu veci, čo sme už použili v iných metódach, takže nie je potrebné kód znova vysvetľovať. Prejdeme opäť do Postman, na novej záložke vyberieme metódu PUT, pošleme ju na koncový bod http://localhost:3000/api/movies/3 a do tela vložíme nasledujúce JSON:

{
    "name": "Star Wars IV",
    "year": 1977
}

Nezabudnite vybrať raw a typ JSON. A potom len overte, pomocou GET požiadavke, že filmy už majú správne nastavený rok premiéry.

Metóda DELETE

A zostáva nám metóda DELETE. To už teraz bude jednoduché:

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);
    }
});

Kódu už určite rozumiete. Nezabudnite si pomocou Postman poslať DELETE požiadavku, aby ste otestovali, že to naozaj funguje.

Nabudúce, v lekcii Úvod do MongoDB , si povieme niečo o databázach a hlavne o MongoDB.

Opäť poznámka na záver: ES6 object destructuring

Ak ste si nevedeli rady s premennou error deklarovanou v zložených zátvorkách, jedná sa tzv. Object destructuring. Miesto vysvetľovanie vám radšej ukážem krátky kód:

const obj = {
    a: 1,
    b: 2,
    c: 3
};
const { a, b } = obj;

V konštantách a a b budú uložené údaje, ktoré sa prečítajú zvnútra objektu. Teda v a bude po prebehnutí kódu uložená jednotka a v b dvojka.


 

Stiahnuť

Stiahnuté 50x (1.27 kB)
Aplikácia je vrátane zdrojových kódov v jazyku JavaScript

 

Predchádzajúci článok
Rozbehnutie projektu a prvé riadky v Expressu
Všetky články v sekcii
Node.js
Článok pre vás napísal Petr Sedláček
Avatar
Ako sa ti páči článok?
Ešte nikto nehodnotil, buď prvý!
Aktivity (1)

 

 

Komentáre

Avatar
Míla Skipper Moravec:26.4.2019 18:28

Stále jsem přemýšlel, proč mi nefunguje metoda POST. A odpověď je, že na tvým řádku máš

app.post('api/movies', (req, res) => {

ale má být

app.post('/api/movies', (req, res) => {
 
Odpovedať
26.4.2019 18:28
Avatar
Odpovedá na Míla Skipper Moravec
Petr Sedláček:26.4.2019 18:38

Vidíš, díky!
Opravím to.

 
Odpovedať
26.4.2019 18:38
Avatar
jindrich.jokel:15.5.2019 13:50

super clanky bruh, planuje i nejake dalsi pokracovani?

 
Odpovedať
15.5.2019 13:50
Avatar
Lukáš Korel:5.8.2019 12:38

Jde nějak získat více parametrů při volání GET?, kdybych chtěl vracet objekt, jehož vyhledání závisí na 2 parametrech (pro jednoduchost výpočet obsahu obdélníku, API by bralo 2 parametry např. http://localhost:3000/…ngle/a=2,b=3 (nebo jak jinak to zapsat)

Děkuji za informaci

 
Odpovedať
5.8.2019 12:38
Tento výukový obsah pomáhajú rozvíjať nasledujúce firmy, ktoré možno hľadajú práve teba!
Avatar
Odpovedá na Lukáš Korel
Tomáš "chlastyml" Kacálek:10.9.2019 15:03

Ahoj Lukasi,
jednoducha odpoved: NE! :D

Slozitejsi popis: Zde se jedna o url adresu. Teoreticky muzes vytvorit routu, kde budes mit v ceste dva parametry timto zpusobem (oddelene pomoci / ):

app.get('/:first/:second', (req, res) => {
    console.log(req.params.first);
    console.log(req.params.second);
    //...

Ale doporucuji ti to nepouzivat! Ja vlastne nedoporucuji vubec cpat informace do url parametru.
**Proc? **
Nejdrive si musime vysvetlit, ze express funguje na principu prvni deklarace vyhrava. To znamena, ze kdyz definuji routu (napr.: pro status a odeslu 'OK')

router.get('/status', (req, res) => {
    res.send('OK');
});

pote nekde dal v kodu definuji routu se stejnou cestou napr.

router.get('/status', (req, res) => {
    res.send('KO');
});

Tak po zavolani teto routy se spusti vzdy ta prvni definovana (tj. vzdy se vrati 'OK');
Ale proc to vubec rikam:
Kdyz se definuje routa s promenym parametrem napr:

app.get('/api/movies/:id', (req, res) => {
    // some logic
});

a pod to definujes treba:

app.get('/api/movies/all', (req, res) => {
    // some logic
});

Tak pro tu all se ti nikdy nevykona. To by si je musel definovat opacne, nebo udelat neco takoveho:

app.get('/api/movies/:id', (req, res, next) => {
    if(req.params.id === 'all') {
        next();
    } else {
        // some logic
    }
});

ale to je na jine povidani! (takze s tim si nemusis delat hlavu pokud si na to jeste nenarazil. Jiank takhle nejak se pisou ty Middlewares).

Summary: Takze kazdym takovym url parametrem si zavres vetev pri vytvareni route path.


A ted konecne Reseni: To co ty urcite hledas jsou query params. Velice uzitecna vec! jsou to parametry za url adresou (od url oddelene otaznikem a mezi sebou oddelene carkou).
Priklad:

http://localhost:3000/path?param1=value1,param2=value2

a pri definici chovani routy:

router.get('/path', (req, res) => {
    // v promene req.query.param1 je shovane "value1"
    // v promene req.query.param2 je shovane "value2"
});

Tak doufam, ze jsem ti pomohl :) Kdyby neco ptej se :)

PS: jsem se nejak rozkecal xD

Editované 10.9.2019 15:03
 
Odpovedať
10.9.2019 15:03
Avatar
Karel Paulík:29. júna 9:07

Když se požadavky posílají na stejnou adresu, jak server pozná co má udělat.
Např.
get: app.get('/api/mo­vies/:id'
delete: app.delete('/a­pi/movies/:id'
Jak server pozná, jestli má vrátit dotaz na film, nebo film smazat? Pokud by byl get např. app.get('/api/mo­vies_get/:id' a deleta např. app.delete('/a­pi/movies_del/:id' tak by to bylo jané, ale při stejné adrese ne.

 
Odpovedať
29. júna 9:07
Avatar
Radek Veverka
Redaktor
Avatar
Odpovedá na Karel Paulík
Radek Veverka:29. júna 10:12

URL adresa není vše, co se serveru posílá. Hned začátek HTTP requestu obsahuje typ requestu - GET, POST, PUT, DELETE...

 
Odpovedať
29. júna 10:12
Avatar
Karel Paulík:29. júna 10:48

Jj, to jsem si neuvědomil. Díky

 
Odpovedať
29. júna 10:48
Robíme čo je v našich silách, aby bola tunajšia diskusia čo najkvalitnejšia. Preto do nej tiež môžu prispievať len registrovaní členovia. Pre zapojenie sa do diskusie sa zaloguj. Ak ešte nemáš účet, zaregistruj sa, je to zadarmo.

Zobrazené 8 správy z 8.