5.1 MySQL
5.2 MongoDB
5.3 Redis
6.1 Perinteinen backend-sovellus
6.2 REST-sovellus
6.3 GraphQL
6.4 Sovelluksen tietoturva
6.5 MEAN-stack
7.1 Nestjs:n arkkitehtuuri ja sovelluksen rakentaminen
7.2 Mikropalvelut Nestjs:llä
Backend-sovelluskehitys1 (4op) kuuluu Jyväskylän Ammattikorkeakoulun (JAMK) Tietojenkäsittelyn tutkinto-ohjelman (Tiko) vaihtoehtoisiin ammattiopintoihin.
Opettaja Tommi Tuikka, tuito(at)jamk.fi
Muut perustiedot ovat opintojakson Moodle-työtilassa.
Palvelinpuolen sovelluskehitykseksi eli backend-sovelluskehitykseksi kutsutaan palvelinkoneella suoritettavien ohjelmistojen kehitystä. Web-sovellus jakaantuu tyypillisesti palvelinpuoleen ja asiakaspuoleen. Palvelinpuolen sovellus eli backend-sovellus on palvelimella suoritettava ohjelmisto, jossa ohjelmointikielenä voi olla esim. JS/TS, Java tai C#. Asiakaspuolen sovellus eli frontend-sovellus on web-selaimessa suoritettava ohjelmisto, jossa ohjelmointikielenä on yleensä JS/TS. Backend ja frontend muodostavat yhdessä full stack -sovelluksen.
Asiakaskoneiden tehokkuuden parantuessa sovellusten suorittaminen on siirtynyt yhä enemmän asiakaspuolelle, mutta on monia asioita, joita ei voi tehdä asiakaspuolella. Palvelimella suoritetaan mm. tietokantojen käsittely, tiedostojen ja kansioiden käsittely, sähköpostin lähetys, autentikaatio ja vaativa laskenta.
Määritelmät: palvelinpuoli ja asiakaspuoli - Server-side vs. client-side programming languages.
Yleisimmin käytössä olevat web-sovellusten palvelinpuolen alustat ovat tällä hetkellä (k2024) LAMP (kielenä PHP), .NET (C#) ja JakartaEE (Java). Palvelinpuolen sovellusten toteuttaminen Javascriptillä ja Typescriptillä Nodejs-alustalle on kuitenkin jatkuvasti yleistynyt.
Nodejs on tärkein Javascriptiin perustuva palvelinpuolen teknologia. Sen suosio on suurinta moderneissa web- ja mobiilisovelluksissa. Nodea haastamaan on noussut myös uusia palvelinpuolen JS-ympäristöjä, kuten Deno.js ja Bun.js, mutta tällä kurssilla käytetään vain Node.js:ää. Express on yleisin Nodejs-sovelluskehys, mutta sen haastajaksi on nousemassa Nestjs, joka on rakennettu osittain Expressin päälle. Alla olevassa kuvassa on esitetty palvelinpuolen tyypillisiä JS-teknologioita. Asiakaspuolen sovellukseen liittyvät osat on "himmennetty".
Serverless-teknologiat ovat yleistyneet viime vuosina erittäin paljon. Serverless-kehitys on palvelinpuolen sovelluskehitystä, jossa palvelin on pilvipalvelualustalla "abstraktoituna" eli kehittäjälle näkymättömissä. Palvelinsovellukset voidaan toteuttaa hyödyntämällä valmiita palveluita pilvialustan tarjoamien rajapintojen/metodien avulla. Serverless-funktioilla on mahdollista muokata ja yhdistellä alustan palveluita. Nodejs:ää käytetään paljon Serverless-funktioiden kehittämiseen eri alustoille, joista tunnetuimpia ovat AWS, Azure ja Google Cloud.
Tämän kurssin tarkoituksena ei ole perehtyä tarkemmin Serverless-sovelluskehitykseen, mutta sitä hyödynnetään esim. Google Cloudin autentikaatiopalvelua käytettäessä. Kurssilla kokeillaan Node/Express-sovelluksen julkaisua Netlify-palvelussa, joka on lähinnä tavallinen pilvihostauspalvelu, vaikka siinä on myös serverless-alustan piirteitä. Serverless-sovelluskehitykseen perehdytään kurssilla Backend-sovelluskehitys2, joka on tämän kurssin jatkokurssi.
Tällä kurssilla tutustutaan palvelinpuolen teknologioista ainoastaan Nodejs:ään, mutta kurssilla esiintyvät käsitteet ja tekniikat ovat sovellettavissa myös muihin palvelinteknologioihin. Esim. arkkitehtuuriratkaisut ja sovelluskehykset ovat palvelinpuolen ohjelmointikielten eroista huolimatta hyvin samantapaisia. Node-sovelluskehitystä opiskelemalla opit siis palvelinpuolen sovelluskehityksen perusteet ohjelmointikielestä riippumatta, ja voit käyttää kielenä asiakaspuolelta tuttua Javascriptiä tai Typescriptiä.
Nodejs on Chrome-selaimen Javascript-moottori irroitettuna selaimesta. Node sisältää lisäksi palvelinohjelmointia varten kehitettyjä kirjastomoduuleja, joita ei ole selaimessa toimivassa JS:ssä. Noden avulla voidaan luoda helposti, muutamalla Javascript-koodirivillä, pieni ja kevyt web-palvelin. Nodella kehitetyt web-sovellukset sisältävät aina web-palvelimen. Sen sijaan Node-sovellukset, jotka eivät ole web-sovelluksia, eivät sisällä web-palvelinta.
Node toimii web-sovelluksissa samaan tyyliin kuin esim. PHP eli sillä voi esim. tehdä kyselyitä tietokantaan ja välittää sieltä dataa käyttöliittymään ja toisinpäin. Erona on se että Noden input/output eli datan vastaanotto ja lähetys ei pysäytä koodin suoritusta (non-blocking I/O). I/O toimii siis tausta-ajona asynkronisesti. Esim. PHP-koodin suoritus pysähtyy kun odotellaan dataa tietokannasta (blocking I/O). Jos palvelimella on paljon samanaikaisia käyttäjiä, jokaiselle pitää avata PHP-koodin suoritukseen oma säie jotta ei tulisi katkoksia. Node-palvelimen ei tarvitse käyttää useita säikeitä, vaan kaikki tehdään yhdessä säikeessä. Node kuluttaa siten paljon vähemmän palvelimen resursseja kuin vaikkapa Apache+PHP silloin kun sovelluksella on paljon samanaikaisia käyttäjiä.
//Non-blocking tietokantakysely Nodejs:llä
connection.query('SELECT * FROM table;', function (error, result) {
response.end(JSON.stringify(result));
});
/*ohjelman suoritus jatkuu heti tästä vaikka tuloksen saanti kannasta
viipyisi, koska kyselyn tulos tulee callback-funktiolla asynkronisesti*/
//Blocking tietokantakysely PHP:llä
$result = mysqli_query($connection, 'SELECT * FROM table');
//ohjelman suoritus pysähtyy tähän siihen asti että tulos saadaan
$row = mysqli_fetch_row($result);
Monet pilvipalveluina tarjottavat sovellukset - joissa liikkuu paljon dataa ja joissa on paljon samanaikaisia käyttäjiä ja jotka tarvitsevat hyvää skaalautuvuutta (laajennettavuutta) - on toteutettu Nodella. Erityisen hyvin Node sopii DIRT- eli Data Intensive Real Time -sovelluksille. Näitä ovat esim. pelien multiplayer-backendit, WhatsAppin tyyppiset chat-sovellukset joissa liikkuu paljon dataa, Codecademyn kaltaiset oppimissovellukset yms. Codecademy ei ole varsinaisesti reaaliaikasovellus, mutta yhtäikaisia käyttäjiä on todella paljon. Hyvä skaalautuvuus voidaan saavuttaa myös PHP:llä ja muilla kielillä joissa on käytössä blocking I/O, mutta se vaatii tehokasta palvelinten klusterointia ja on kalliimpaa.
Node ilman lisäosia on hyvin pelkistetty matalan tason systeemi. Kunnon sovellusten tekeminen sen päälle onnistuu vasta kun siihen ladataan lisäosia eli moduulikirjastoja npm-pakettienhallintajärjestelmän avulla. Sovellusten kehittäminen Node-alustan päälle perustuu kerrosmaiseen rakenteeseen joka on selitetty alla olevassa kuvassa:
Kaaviokuva Nodejs- web-sovelluksen kerroksista. 1. Alimpana kerroksena on Noden ydin ja ytimeen sisältyvät matalan tason palvelut kuten http. Coren avulla voidaan jo tehdä esim. yksinkertainen web-serveri joka lukee tiedoston sisällön näytölle fs-moduulin avulla. 2. Toinen kerros on Yhteisön moduulit eli lisäosat jotka voi asentaa npm:llä. Esim. express tarjoaa kirjaston web-sovellusten kehittämiseen. 3. Ylin kerros on sovelluslogiikka joka muodostuu alempien kerrosten tarjoamia kirjastoja käyttämällä. Se on kehittäjän työn tulos joka on jokaiselle sovellukselle yksilöllinen.
Ohjelmointiympäristönä käytetään normaalia JS-ekosysteemiä ja kehitysvälineenä Visual Studio Code -editoria. Eli ohjelmointiympäristö on sama kuin aikaisemmilla kursseilla.
-Node_hello. Yksinkertaisia Nodejs-perusesimerkkejä.
Node-sovellusta on kätevintä debugata kehitystyön aikana VSCoden sisäänrakennetun debuggerin avulla. Debuggeri on esitelty ja sitä on käytetty aikaisemilla kursseilla.
-Debugging NodeJS with Chrome and VSCode
-Debugging NodeJS in VSCode
-Debugging asynchronous Javascript
Palvelinpuolen sovelluskehitys Nodella on yleensä asynkronista ohjelmointia. Tämä tarkoittaa sitä että sovelluksen koodia ei suoriteta järjestelmällisesti peräkkäin rivi riviltä ylhäältä alaspäin, vaan vaikka edellisen rivin suoritus olisi vielä käynnissä voidaan aloittaa jo seuraavan rivin suoritus. Tapahtumapohjaisten käyttöliittyminen koodaaminen on yleisin esimerkki asynkronisesta ohjelmoinnista. Esim. napin painallus laukaisee tietyn koodin suorituksen vaikka muutakin koodia olisi samanaikaisesti suorituksessa. Nodejs toimii palvelinpuolella samalla periaatteella. Noden toiminta perustuu yksisäikeiseen event looppiin. Node-sovellukset ovat siis automaattisesti asynkronisia ja niillä on non-blocking I/O, kunhan ne toteutetaan asynkronisilla koodirakenteilla.
Nodessa asykroninen ohjelmointi on perinteisesti toteutettu callback-funktioiden ja eventtien (tapahtumien) ja niihin liittyvien event listener (tapahtumankuuntelija) -funktioiden avulla. Event listenerit ovat myös callbackeja. Tavallisilla callback-funktioilla käsitellään yhden kerran tapahtuvia asynkronisia tapahtumia ja event listenereillä toistuvia tapahtumia. Node-sovelluksissa voidaan käyttää asynkroniseen ohjelmointiin myös promiseja ja observableja.
Asynkronisen ohjelmoinnin koodirakenteet ovat callback, promise + async/await ja observable. Niillä on omat käyttötarkoituksensa, joita on selvitetty tässä artikkelissa.
-Asynkroninen ohjelmointi kansanomaisella esimerkillä selvitettynä
Callback on funktio joka annetaan parametrina toiselle funktiolle. Callback-funktio suoritetaan kun sen "isäntäfunktio" on suoritettu. Callbackit ovat perinteisin tapa käsitellä asynkronisen tapahtuman suoritus Nodessa. Niillä käsitellään yhden kerran suoritettavia asynkronisia tapahtumia. Callback on yksinkertainen menetelmä asynkronisten tapahtuminen käsittelyyn, joten sitä voi edelleen käyttää, kun ei tarvitse ketjuttaa useita asynkronisia tapahtumia. Useat asynkroniset tapahtumat muodostavat callbackeillä toteutettuna turhan monimutkaisen sisäkkäisen rakenteen josta käytetään myös nimitystä "callback hell".
Event on Node-koodissa määritelty tapahtuma jonka suoritus laukaisee callbackin jota kutsutaan event listeneriksi. Node-sovelluksessa on käynnissä Event loop jossa kuunnellaan jatkuvasti eventtejä ja jos niitä havaitaan, suoritetaan koodia. Event emitter on olio joka tuottaa (emitoi) eventin. Monet Nodejs:ssä esiintyvät oliot kuten server(syntyy net.Server -luokasta) ja socket(net.Socket) ovat automaattisesti event emittereitä, sillä ne perivät events.EventEmitter-luokan. Event emitterin voi luoda myös itse kyseisestä luokasta. Eventtien avulla käsitellään asynkronisia tapahtumia jotka voivat tapahtua useita kertoja peräkkäin.
-Yksinkertainen event-esimerkki.
-Hieman isompi event-esimerkki.
Promise (lupaus) on Javascript-olio joka edustaa asynkronisen operaation tulosta. Promisella voi olla kolme tilaa: pending - alkutila, fulfilled - toteutettu, rejected - hylätty. Promiseja käytetään yleensä siten että asynkronisesta funktiosta muodostetaan promise-olio joka palautetaan. Promise vastaanotetaan ja suoritetaan perinteisesti then()- ja catch() -metodeilla. Async/await-rakennetta käytettäessä Promise voidaan suorittaa yksinkertaisesti laittamalla sen eteen await-avainsana. Promisen hyöty callbackiin verrattuna on lähinnä selkeämpi rakenne käyttövaiheessa ja helpompi ketjutus sekä virheenkäsittely. Promisen käyttö on suositeltavaa silloin jos ilman promisea pitäisi ketjuttaa useita callbackejä. Useimmat Nodejs-kirjastojen metodit palauttavat promisen, joten promisesta on tullut yleisin asynkronisen ohjelmoinnin koodirakenne.
-Node.js Async: How To Use ES6 Promises.
-ES6 Promise-esimerkki.
Async/await-rakennetta käytetään promisejen käsittelyyn. Se on promisen laajennos joka helpottaa niiden käsittelyä mahdollistamalla asynkronisen koodin kirjoittamisen synkronisella tyylillä.
Observablen käyttäminen Nodejs-sovelluksissa onnistuu RxJS kirjaston avulla. Observablesta on hyötyä erityisesti jatkuvana virtana (stream) suoritettavien asynkronisten tapahtuminen käsittelyssä.
-Reactive programming and Observable sequences with RxJS in Nodejs
Asynkroniset tehtävät voidaan pakottaa suoritettavaksi joko peräkkäin tai rinnakkaisesti. Seuraavassa kuvassa on esitetty ensin peräkkäissuoritus (serial flow) ja sitten rinnakkaissuoritus(parallel flow):
Esimerkkinä peräkkäissuorituksesta voisi olla esimerkiksi ohjelma joka luo kansion ja sen jälkeen laittaa tiedoston kansioon. Nämä on pakko suorittaa peräkkäisesti jotta homma toimisi. Esimerkki rinnakkaissuorituksesta voisi olla esim. useiden tiedostojen lataus netistä ja niiden laitto zip-pakettiin. Latauksen tulee olla ajan säästämiseksi rinnakkainen, mutta kaikkien latausten pitää olla valmiita ennen kuin voidaan tehdä zip-paketti. Oikean suoritusjärjestyksen toteuttamiseen on kehitetty npm-moduuleja kuten async ja nimble.
-Esimerkki serial ja parallel flowsta.
Moduuleilla on hyvin tärkeä merkitys Node-sovelluskehityksessä sillä ne muodostavat Node-sovellusten rakenteen perustan. Noden ydin on hyvin pienikokoinen(kevyt) ja lisäominaisuudet tuodaan moduuleina jotka joko ladataan valmiina npm-pakettivarastosta tai tehdään itse. Omasta koodista ei ole pakko tehdä modulaarista mutta se on suositeltavaa. Moduuleista on hyötyä koodin uudelleenkäytössä ja testauksessa. Node-sovelluksissa on perinteisesti käytetty Commonjs-moduulisysteemiä. Nykyään Nodessa on täydellinen tuki myös ES6:n ja uudempien ES-versioiden ESModules-standardille. Sitä käytettäessä on kuitenkin muistettava lisätä package.json:iin määritys "type": "module".
Moduulit voivat olla yksittäisiä .js-tiedostoja tai kansioita jotka sisältävät tiedostoja. Kansion tulee sisältää index.js -tiedosto josta moduuli käynnistyy tai sitten käynnistys/päätiedosto pitää määritellä package.json-tiedostossa main-avaimella.
Node-sovellus etsii require- (Commonjs) tai import (ESModules) -määreellä käyttöön otettuja moduuleja määrätyssä järjestyksessä. Ensin samasta hakemistossa jossa sovellus on, sitten node_modules -kansiosta. Jos kummastakaan ei löydy, mennään ylemmän tason kansioon etsimään.
-Nodejs module patterns
-Esimerkki itse tehdyistä
moduuleista ja niiden käytöstä
Node-sovellusten tyypillisiä tietovarastoja ovat relaatiotietokanta MySQL sekä NoSQL-tietokannat MongoDB ja Redis. NoSQL-kannat ovat moderneissa web-sovelluksissa relaatiotietokantoja yleisempiä. Tietokantojen käsittelyyn on olemassa paljon erilaisia npm-kirjastoja.
MySQL on yleisimmin käytetty relaatiotietokanta. MySQL:n tai sen klooni MariaDB voidaan asentaa suoraan käyttöjärjestelmän päälle esim. asennusta helpottavien MAMP- tai XAMPP -pakettien avulla. Docker-konttijärjestelmä tarjoaa käyttöjärjestelmästä eristetyn ajoympäristön, joten sen käyttäminen on suositeltavampaa. Docker Desktop -sovellus on helppo asentaa eri käyttöjärjestelmille asennusohjelman avulla. Konttien asennus tapahtuu helpoiten ajamalla docker-compose.yml -tiedosto komentokehotteessa.
MySQL:n käsittely Nodella voidaan toteuttaa mysql2-kirjaston avulla. Kirjastolla voidaan hoitaa kaikki yleisimmät tietokannan käsittelytoimenpiteet. Noden mysql2-kirjaston funktiot voivat palauttaa tulokset joko callbackissa tai promisena.
-MySQL-esimerkki Noden mysql2-kirjastolla toteutettuna.
Sequelize
Sequelize on Noden yleisin ORM-kirjasto (Object Relational Mapping), jota voidaan käyttää useiden relaatiotietokantojen kanssa. ORM-kirjasto mahdollistaa kantaan tallennettavan tiedon käsittelyn olioina eli yhdistää oliodatan ja relaatiotietokannan taulujen datan. ORM-kirjasto tarjoaa myös datamallin eli skeeman koodissa esiintyville olioille, jolloin jo koodipuolella varmistetaan tiedon oikeellisuus. Kannan käsittely on yleensä abstraktoitu, eli koodissa ei tarvitse käyttää SQL-lauseita. Kantaa siis käsitellään ORM-kirjaston omilla metodeilla. ORM-kirjasto tekee koodin vähemmän riippuvaiseksi käytetystä tietokannasta, jolloin kannan vaihto tarvittaessa on helpompaa.
Modernissa web- ja mobiilikehityksessä relaatiotietokantoja käytetään hyvin usein pilvipalveluista. Kannan ylläpito on tällöin ulkoistettu kokonaan palveluntarjoajalle. Esimerkiksi AWS:n RDS-palvelun kautta voi käyttää useita eri relaatiotietokantoja. Kaikki organisaatiot eivät kuitenkaan voi tietoturvasyistä ulkoistaa kaikkia tietokantapalvelimiaan. Esim. KELA, sairaalat, poliisi, puolustusvoimat yms. ovat tällaisia.
MongoDB on yleiskäyttöinen NoSQL-kanta. Se sopii tiedon pitkäaikaiseen varastointiin ja joissain tapauksissa relaatiotietokannan korvaajaksi. Mongodb koostuu kannasta, kokoelmista eli collectioneista ja niiden sisältämistä dokumenteista, jotka muodostuvat JSON-muotoisista avain-arvo -pareista. Mongodb ja muut dokumenttitietokannat eivät pysty takaamaan kaikissa olosuhteissa samanlaista tietojen eheyttä (ACID -periaate) kuin relaatiotietokannat. Mutta esim. sellaisissa web- ja mobiilisovelluksissa joissa tietojen eheys ei ole elintärkeää, Mongodb tai muut NoSQL-kannat kuten Amazonin Dynamodb tai Firebasen Firestore ovat nopeita, tehokkaita, joustavia ja helposti skaalautuvia vaihtoehtoja sovelluksen datan säilyttämiseen. Mutta esim. pankkisovellukset, verkkokaupat tai sairaalajärjestelmät vaativat aina relaatiotietokannan.
-MongoDB:n ja MySQL:n vertailu ja suositeltavat käyttökohteet
Mongon käyttöön on tehty monia npm-apukirjastoja. Mongodb on perusajurikirjasto, mutta webissä olevissa esimerkeissä Mongoa käytetään yleisimmin Mongoose-kirjaston avulla.
Mongoose
Mongoose on ODM-kirjasto (Object Document Modeling/Mapping) joka tarjoaa ORM:ia vastaavat toiminnot dokumenttitietokannalle, eli tallennettavan tiedon mallin (schema), tiedon käsittelyn olioina ja kyselyiden abstraktiometodit. Mongoosen kyselymetodit ovat Model-luokan tai siitä luodun olion metodeja. Mongoosen kyselyt ovat hieman erilaisia ja muodoltaan yksinkertaisempia, kuin Mongon oikeat tietokantakyselyt. Mongoosesta on tullut suosittu erityisesti sen tarjoaman skeeman ansiosta. Mongon käyttö on turvallisempaa kun tietojen tyypit ja muodot on määritelty skeemassa.
HUOM! Mongoosea käytettäessä täytyy ottaa huomioon yksi "omituinen" sääntö. Mongodb-collectionin nimen pitää olla aina sama kuin mongoose-modelin nimi pienellä alkukirjaimella + s. Eli jos meillä on model nimeltään Student, jolloin collectioniin tallennetaan Student-tyyppisiä dokumentteja, niin collectionin nimen täytyy olla students. Jos nimeämiskäytännettä ei noudata, Mongoose-kysely ei löydä dataa, vaan palauttaa esim. tyhjän taulukon.
-Introduction to Mongoose for Mongodb
MongoDb asennus
Mongodb voidaan asentaa omalle työasemalle suoraan käyttöjärjestelmän päälle tai Docker-kontissa. Docker-kontti käyttöjärjestelmästä eristettynä ajoympäristönä on suositeltavampi ratkaisu. Hyviä käyttöliittymäsovelluksia kantojen tarkasteluun ovat MongoDb Compass ja Robo3T, jotka ovat hieman kätevämpiä kuin seuraavan ohjeen mukaisen asennuksen mukana tuleva Mongo-Express.
Huomaa että suoraan käyttöjärjestelmän päälle asennetun Mongon tietokannoilla ei ole oletuksena lainkaan käyttäjätunnusta ja salasanaa. Tämä ei haittaa silloin kun ajat kokeilusovelluksia omalla koneellasi, mutta tuotantokäytössä tunnarit pitää tietysti olla olemassa. Niiden käyttöönotto on esitetty tässä oppaassa. Usein sovelluksen tietokanta laitetaan MongoDB-pilvipalveluun kuten MongoDBAtlas:iin. Tällöin tunnarit saadaan palveluntarjoajalta. Node-sovelluskehityksessä hyvä käytänne on laittaa tunnarit .env-tiedostoon ympäristömuuttujiksi, josta ne haetaan tiedostoon, jossa yhteydenotto kantaan tapahtuu. Jotta .env-tiedosto saadaan käyttöön, pitää sovellukseen ladata dotenv-moduuli.
-Mongodb-basics -esimerkit
(Mongon käyttö ilman Mongoose-kirjastoa).
-Mongoose-basics -esimerkki
(käytössä .env-tiedosto).
Redis on NoSQL-kanta joka poikkeaa Mongosta siten, että dataa säilytetään muistissa eikä kiintolevyllä. Dataan tehdyt muutokset tallennetaan lokiin, joka on kiintolevyllä. Jos data häviää muistista, se voidaan palauttaa lokista. Data muodostuu tietorakenteista, joilla ei ole samanlaista hierarkiaa kuin esim. Mongossa. Redisin tietorakenteita ovat: string, list, hash table ja set. Redistä ei välttämättä voi kutsua ihan "oikeaksi" tietokannaksi, vaan se on tarkkaan ottaen koneen muistissa sijaitseva tietorakennevarasto.
Redis on tarkoitettu sellaisen datan varastointiin, jota tarvitsee säilyttää suhteellisen lyhyen ajan, mutta jota pitää pystyä käsittelemään hyvin nopeasti. Esim. tietokonepeleissä ja muissa reaaliaikasovelluksissa esiintyvä data on monesti tämän tyyppistä. Redistä käytetään paljon välimuistina (cache), eli "oikeasta" tietokannasta haettu data välivarastoidaan Redisiin, ja asiakas käyttää dataa Redisin kautta. Redisiä käytetään usein esim. mikropalveluiden datan välivarastointiin ja datan välittämiseen asiakkalle Redis pub/sub-protokollalla, sekä varastoimaan Nodejs-serverin sessiodataa.
-Redis asennusohje.
-Redis Desktop Manager on yksinkertainen graafinen käyttöliittymä Redisille.
Web-sovellusten kehittämiseen Nodella on olemassa paljon sovelluskehyksiä. Ne jakautuvat MVC-sovellukehyksiin, REST-sovelluskehyksiin ja full-stack -sovelluskehyksiin. MVC-sovelluskehys tarjoaa MVC-mallin tai vastaavan rungon, jonka päälle web-sovelluksen backend voidaan rakentaa. REST-sovellukehykset tarjoavat rungon pelkästään REST API -backendille. Full-stack -sovelluskehykset tarjoavat alustan ja työkalut sekä backendin että frontendin rakentamiseen.
Yleisin web-sovellusten kehittämiseen tarkoitettu Nodejs backend-framework on Express (4.x API), joka luokitellaan MVC-sovelluskehykseksi. Myös REST API -backendin toteuttaminen on sen avulla helppoa. Express tarjoaa minimaalisen web-sovelluskehyksen jota voidaan laajentaa npm-moduleilla. Expressin toiminta perustuu ns. middleware-funktioihin, jotka ovat funktioita, joiden kautta voidaan käsitellä HTTP-protokollan pyyntöjä (request) ja vastauksia (response). Express sisältää valmiiksi paljon middleware-funktioita ja lisää voidaan asentaa erillisinä npm-kirjastoina. Express käyttää Noden http-kirjastoa web-palvelimen luontiin. Express tarjoaa tuen myös backendin käyttöliittymälle (view) ja backend-sovelluksen sisäiselle reititykselle (router).
Express asennetaan ilman valmista tiedosto- ja kansiorakennetta komennolla npm i express. Express-generator -sovellusrunkogeneraattorilla voidaan luoda sovellukselle valmis tiedosto- ja kansiorakenne. Expressille on tehty myös modernimpi Express-generator-typescript -sovellusrunkogeneraattori. Typescriptin käyttäminen Express-sovelluskehityksessä on suositeltavaa, mutta koska kurssilla keskitytään backend-kehityksen perusteisiin, ja kaikki muu yritetään pitää niin yksinkertaisena kuin mahdollista, käytämme toistaiseksi Expressiä perinteisesti Javascriptillä.
-Express-perusesimerkki (tehty ilman Express-generatoria)
Express-generator rakentaa sovellukselle kansiorakenteen ja alkeellisen MVC-mallin. Generator ei luo puhdasta kontrolleria, vaan router toimii reititystehtävän ohella kontrollerina. Generatorin luomaan runkoon voi itse luoda erillisen kontrollerikansion ja sinne kontrollerit. Jos sovelluksessa käytetään backendin viewiä, otetaan käyttöön template-engine: esim. EJS. Templaattisivut muodostavat viewin. Useimmiten Expressin avulla tehdään REST-backend, eli backendin REST-serveri välittää JSON-dataa erilliseen frontendiin. Tällöin sovelluksessa ei tarvitse olla minkäänlaista viewiä, eli EJS:ää tai vastaavaa template-engineä ei tarvita.
Kehittäjän ei välttämättä tarvitse käyttää Express-generatorin valmiina tarjoamaa kansiorakennetta vaan sitä on myös mahdollista muuttaa paremmin omaan tarkoitukseen sopivaksi: Best practices for Express app structure. Kaikki Express-tiedostot ja mahdolliset kansiot voi luoda myös itse.
Express-sovellusten kehittäminen perustuu erillisten lisämoduulien eli npm-kirjastojen käyttöön. Seuraavassa esimerkkejä tyypillisistä backend-toiminnoista toteutettuna Expressin ja sen lisämoduulien avulla:
-Sessio: express-session, tutoriaali
-Cookie: cookie-parser, tutoriaali
-Sähköpostin lähetys: nodemailer,
tutoriaali
-Palvelimelle saapuvan datan validointi: validator,
tutoriaali
-Uploadaus eli tiedoston lataus palvelimen kansioon: formidable,
tutoriaali
-JWT-token: jsonwebtoken, tutoriaali
-Autentikaatiostrategia: passport,
tutoriaali
Lisämoduuleja on paljon, ja jokaiselle edellä esitetyistä moduuleista on olemassa myös useita muita vaihtoehtoja.
Nodella, Expressillä ja Mongolla/MySQL:llä tehdään edelleen myös perinteisiä MVC-mallia noudattavia backend-sovelluksia, joilla ei ole lainkaan frontendiä. Käyttöliittymä tehdään tällöin Express -sovellusrungon views-kansiossa sijaitseviin templaatteihin eli kyseessä on backendin view. Templaatit voivat toimia täysin perinteisesti, jolloin HTTP-pyynnöillä ladataan koko sivu, tai niissä voidaan käyttää XHR-olioita tai fetch()-metodia, joiden avulla sivuille voidaan ladata osia asynkronisesti. Joka tapauksessa sovelluksessa ei ole erillistä frontendiä vaan käyttöliittymä sijaitsee backendin templaateissa, joissa voi olla selaimelle suoritettavaksi siirtyvää Javascriptiä.
REST API -backendin kehittäminen on ehkäpä yleisin Noden, Expressin ja Mongodb:n/MySQL:n käyttökohde. REST-sovellus tarkoittaa sovellusta jonka dataa voi manipuloida URL-osoitteen kautta. URL-osoitteisiin meneminen laukaisee backendin metodeja. Backendissä voidaan suorittaa CRUD-toiminnot jotka perustuvat HTTP:n POST, GET, PUT ja DELETE -metodien käyttöön. REST-sovelluksessa on palvelinpuoli eli REST-serveri ja asiakaspuoli eli REST-client. REST-serverin tarjoamaa rajapintaa dataan kutsutaan REST API:ksi. API-lyhenne tulee sanoista Application Programming Interface. Palvelinpuolen web-sovelluksella ei tarvitse olla välttämättä lainkaan käyttöliittymää, vaan REST-serveri tarjoaa ainoastaan REST APIn toiselle/toisille sovelluksille joilla on käyttöliittymä.
REST-serveri ei välttämättä tarvitse kokonaista Express-generator -sovellusrunkoa, vaan esim. views- ja public-kansiot voi poistaa. REST-serveri voidaan luoda myös jonkin pelkästään REST APIn kehittämiseen tarkoitetun npm-kirjaston avulla, esim. Restify. Yritysten tarjoamat REST APIt noudattavat usein Open API Spesification -standardimuotoa. OAS-standardin mukainen API voidaan kehittää Swagger-frameworkin avulla.
REST APIa voidaan testata ilman itse tehtyä frontendiä Postman-clientilla, joka on työpöytäsovellus. Jos lähetät (POST) yksinkertaisia avain-arvo -pareja Postmanilla, valitse Body-valikko ja sieltä x-www-form-urlencoded -muoto ja anna lähetettävät avain-arvo -parit. Jos lähetettävä data on monimutkaisempaa valitse body-valikosta raw ja lähetettävän tiedon muodoksi JSON. Raw on "raakaa" dataa ja tässä tapauksessa JSON-muotoista eli kirjoitat/copypastaat esim. tietokantaan lisättävän opiskelijan tiedot JSON-muodossa.
GraphQL on kyselykieli jolla haetaan palvelimelta ainoastaan asiakassovelluksessa tarvittava data. Palvelimelle tehdään GraphQL-API, johon kyselyt kohdistetaan. GraphQL:n etuna RESTiin verrattuna on se, että turha datan siirto ja JSON-tiedostojen parsimisen tarve vähenevät. GraphQL-kysely tehdään RESTistä poiketen vain yhteen endpointiin. GraphQL:n käyttöön liittyy myös ongelmia, joiden vuoksi sitä ei vielä suositella käytettäväksi kaikkien APIen kehitykseen. GraphQL vs. REST -artikkelissa on pohdittu GraphQL:n hyviä ja huonoja puolia. GraphQL on REST-API:a täydentävä vaihtoehto tietynlaisten API:en kehittämiseen, mutta se ei ole REST-API:n korvaaja. REST-API on edelleen huomattavasti suositumpi kuin GraphQL.
Yksinkertainen GraphQL-serveri toteutettuna express-graphql -kirjastolla:
Huomaa, että express-graphql -kirjasto on vanhentunut (deprecated), joten se ei sovellu tuotantokäyttöön, kuten esim. alla olevassa esimerkissä esitetty Apollo-serveri. Express-graphql -esimerkki kuitenkin toimii edelleen, ja soveltuu GraphQL:n toiminnan havainnollistamiseen esim. opetuskäytössä.
Tuotantokäyttöön tarkoitettu GraphQL-serveri voidaan toteuttaa Node-alustalle esim. @apollo/server-kirjaston avulla. GraphQL-API voidaan luoda myös suoraan Mongoose-skeemasta graphql-compose-mongoose -kirjaston avulla. Tämä on helpoin tapa toteuttaa GraphQL-API Mongoosea käyttävälle Node-sovellukselle.
Yksinkertainen GraphQL-serveri toteutettuna @apollo/server-kirjastolla:
-@apollo/server GraphQL-esimerkki
Jos edellisen esimerkin GraphQL-API halutaan toteuttaa osana Express-sovellusta, tulee Apollo-kirjaston mukana sitä varten expressMiddleware-kirjasto, jonka avulla Apollo yhdistetään Expressiin. Tässä dokumentaatio sen käyttämiseen.
Kaikessa web-sovelluskehityksessä on otettava huomioon sovelluksen tietoturva, sillä web on erittäin turvaton ympäristo ohjelmistoille. Tärkeimmät kehittäjältä vaadittavat toimenpiteet ovat sisään tulevan tiedon tarkistus, kriittisen datan piilotus ja kryptaus, autentikaatio ja authorisaatio sekä HTTPS-protokollan käyttö.
Kaikki sovellukseen sisään tuleva tieto - eli lomakkeille kirjoitettava ja kantaan lähetettävä tieto - tulisi validoida eli sen oikeellisuus pitäisi tarkistaa. Esimerkiksi jos syöttökenttään pitää kirjoittaa nimi, voidaan syötetty data filtteröidä säännönmukaisilla lausekkeilla esim. niin, että käyttäjä ei voi syöttää kuin enintään 50 kappaletta kirjaimia a-ö. Validointi olisi hyvä tehdä varmuuden maksimoimiseksi asiakaspuolen validoinnin lisäksi myös palvelinpuolella.
Yleiskirjasto validator soveltuu kaikkien JS-sovellusten lomakkeiden validointiin sekä asiakas- että palvelinpuolella. Frontend-sovelluskehyksissä voi olla omat menettelytapansa lomakkeiden validointiin. Hyvin tehty validaatio ehkäisee SQL-injektiot, XSS-hyökkäykset ja muut syöttökenttien kautta sovelluksiin tehtävät hyökkäykset. Validator soveltuu myös REST API:n kautta sovellukseen tulevan datan validointiin (tutoriaali). Jos REST API:lla manipuloidaan vain tietokantaa, erillistä validaattoria ei välttämättä tarvita, sillä sekä NoSQL- että relaatiotietokanta sisältävät yleensä datan rajoitteet ja validoinnin. Esim. Mongoose-skeeman validointi estää vääränlaisen datan kirjoittamisen kantaan.
Jos sovellus sisältää tunnuksia, salasanoja, salausavaimia yms. dataa jonka vuotaminen hakkerille voi vaarantaa sovelluksen turvallisuuden, pitää tällainen data piilottaa niin ettei sitä voi lukea suoraan sovelluksen lähdekoodista. Sen voi sijoittaa esim. palvelimen ympäristömuuttujiin, tietokantaan tai tiedostoon, joka ei sijaitse julkisesti webbiin näkyvässä kansiossa. Nodejs:ssä yleisin hyvä käytänne on laittaa tällainen data .env -tiedostoon ympäristömuuttujiksi.
Lisäksi sovelluksessa esiintyvien salasanojen tulisi olla kannassa salakirjoitettuina. Tässä on ohje salasanojen suojaukseen bcrypt-kirjastolla. Sama asia hoituu käyttämällä bcryptjs-kirjastoa. Myös argon2 on paljon käytetty salauskirjasto.
-Node/Express-esimerkki salauksesta ja validoinnista palvelinpuolella
Edellä tehty REST API muuten kyllä toimii, mutta kuka tahansa pääsee url-osoitteiden kautta muokaamaan tietokantaa. Autentikaatio (authentication) tarkoittaa käyttäjän tunnistamista ja authorisaatio (authorization) tarkoittaa pääsynvalvontaa eli käyttöoikeuksien antamista käyttäjälle. Näitä kahta asiaa käsitellään usein yhtenä kokonaisuutena autentikaatio-nimikkeen alla. REST APIlle pitää luoda autentikaatio joko perinteiseen tyyliin session avulla (kts. tehtävä 3) tai JWT:llä (JWT Wikipedia). Moderneissa SPA-sovelluksissa käytetään JWT:tä eli tokeniin perustuvaa autentikaatiota, sillä sessiot eivät toimi SPA-sovellusten kanssa. JWT:llä on muitakin etuja sessioon verrattuna, joten siitä on tullut paras vaihtoehto uusissa sovelluksissa.
Tokeneissa tai sessioissa ei pitäisi säilyttää dataa, joka kertoo käyttäjästä yhtään enempää kuin on tarpeen. Esim. frontendin pääsynvalvontaan tarkoitettu token voi sisältää tiedon siitä onko käyttäjä oikeutettu pääsyyn esim. tiettyyn SPA-sovelluksen näkymään, kuten { isAdmin: true }. Käyttäjätunnuksia, salasanoja, mailiosoitteita yms. ei kannata turhaan säilyttää tokenissa tai sessiossa asiakaskoneen selaimella, jonne voi olla pääsy muillakin, kuin henkilöllä jonka käyttöön kyseinen tieto on tarkoitettu.
Nykyään autentikaatio ja authorisaatio toteutetaan yhä useammin ulkoisen palvelun avulla (esim. Google, Facebook yms.). Tätä kutsutaan sosiaaliseksi loginiksi (social login) tai sosiaaliseksi autentikaatioksi, Kts. Sosiaalinen login backendissä.
Selaimet määrittelevät web-sivun ei-turvalliseksi jos SSL eli HTTPS-protokolla ei ole käytössä. HTTPS-sertifikaatin asennus palvelimelle on palvelintilan tarjoajan tehtävä, mutta HTTPS:n käyttöönotto sovelluksessa voi olla kehittäjän tehtävä. Node-palvelimella HTTPS:n käyttöönotto onnistuu helposti korvaamalla http-moduuli https-moduulilla. Huomaa että kehitystyössä omalla koneella ei voi käyttää https-moduulia ellei omalle koneelle ole asennettu HTTPS-sertifikaattia. Nykyaikaiset palveluntarjoajat kuten esim. Netlify, Heroku yms. uudelleenohjaavat HTTP-alkuisiin osoitteisiin tehdyt pyynnöt HTTPS-alkuisiin osoitteisiin eli "pakottavat" SSL:n päälle.
-Running express.js server over https
HTTPS:n tärkein tehtävä on salata data joka lähetetään asiakassovelluksen lomakkeilta palvelimelle HTTP-protokollan välityksellä. Se on vain yksi osa sovelluksen tietoturvaa ja "Turvallinen"-merkintä selaimen osoiterivillä ei tarkoita sitä että sovellus olisi turvallinen jos muut edellä esitetyt tietoturvaseikat on laiminlyöty.
MEAN-stack on full stack, jossa MongoDB, Express, Angular ja Node toimivat yhdessä. MEAN-sovellus luodaan tekemällä Expressillä kehitetylle REST APIlle Angular-frontend. Angular-frontend voidaan sijoittaa Express-sovelluksen public/javascripts-kansioon tai se voi sijaita jossain muualla palvelinpuolella tai kokonaan eri palvelimella kuin palvelinsovellus. Angular-asiakassovellus siirtyy selaimelle suoritettavaksi kun sovelluksen käyttö alkaa.
Jos Angular-asiakassovellus haetaan eri palvelimelta, kuin missä palvelinsovellus sijaitsee, tiedon siirtämisessä asiakas- ja palvelinsovellusten välillä tulee ongelmia koska selaimet noudattavat ns. same-origin-policy -sääntöä. JSON-data ei kulje tietoturvasyistä sellaisten sovellusten välillä joilla ei ole sama 'origin'. Expressissä on tähän helppo ratkaisu. Cors -moduulin (cross origin resource sharing) avulla voidaan Express-sovelluksen päätiedostossa parilla koodirivillä sallia datan kulku eri osoitteista peräisin olevien sovellusten kesken. Resurssien jako voidaan sallia myös vain tietyistä osoitteista tai tietyistä serverin routeista.
MEAN-stackin asiakaspuolelle eli Angular-sovellukseen pitää useimmiten tehdä myös autentikaatio. SPA-sovelluksen autentikaatiota on käytännössä mahdotonta tehdä järkevästi perinteisellä sessioihin ja cookieihin perustuvalla menetelmällä. Token-perustainen autentikaatio on tällöin ainoa järkevä autentikaatiomenetelmä.
Tyypillisessä full stack -sovelluksessa frontendin napeilla ja säätimillä käytetään backendin metodeja. Edellä esitetyssä MEAN-stack -sovelluksessa käytettiin Express-backendin tietokantaa käsitteleviä metodeja Angular-käyttöliittymän napeilla ja linkeillä. Ne laukaisivat Angularin service-tiedostojen sisältämiä asynkronisia metodeja jotka puolestaan laukaisivat backendin metodeja. Tämä perustui siihen että backendissä on REST API joka kykenee vastaanottamaan pyyntöjä frontendistä. REST API voi sisältää muitakin kuin tietokantaa käsitteleviä metodeja jotka laukaistaan frontendistä; esim. tiedostojen lataus, sähköpostin lähetys yms.
Backin ja frontin yhdistäminen REST APIlla toimii silloin kun niiden välistä yhteyttä tarvitaan silloin tällöin, esim. lisätään tietoa ja haetaan tietoa silloin tällöin, mutta yhteyden ei tarvitse olla päällä jatkuvasti. Jos backin ja frontin välillä pitää olla jatkuva reaaliaikainen yhteys, kannattaa käyttää websocket-protokollaa esim. Socket.io:n avulla. Kts. luku8.
Nykyään autentikaatio tehdään yhä useammin sosiaalisen median palvelun tarjoamien käyttäjätietojen perusteella. Sovelluksen kehittäjän ei tarvitse ryhtyä keräämään käyttäjärekisteriä omaan tietokantaansa, jos käyttäjätiedot voidaan saada Googlelta, Microsoftilta yms. Loggautumista sovellukseen sosiaalisen median tunnuksilla kutsutaan sosiaaliseksi loginiksi. Sen toteuttamiseksi kehittäjän pitää ensin rekisteröityä palveluntarjoajan kehittäjäsivuille tai pilvipalvelualustalle. Palvelussa luodaan login-applikaatio jolle annetaan sen sivun tiedot jossa sosiaalista loginia käytetään. Oman sovelluksen käyttöliittymän login-sivu ottaa yhteyttä palveluntarjoajan login-applikaatioon joka palauttaa kirjautumisen onnistuttua käyttäjän profiilitiedot ja access tokenin jolla voidaan tarvittaessa käydä login-applikaatiossa tarkistamassa että käyttäjä on oikea. Yksinkertainen sosiaalinen login Google-tunnuksilla on tehty Express-passport-google-1 -esimerkissä. Esimerkki sisältää ohjeen login-applikaation luontiin Google Cloud -alustalle.
Node-sovellusten autentikaation apuna käytetään usein passport-moduulia, jonka kanssa voi käyttää monia erilaisia autentikaatiostrategioita, esim. Googlen, Microsoftin tai OpenID -strategiaa. Autentikaatiostrategiat käyttävät yleensä OAuth(2)-standardia/protokollaa joka antaa sovellukselle käyttöoikeudet käyttäjän tietoihin, jotka saadaan esim. Googlelta. Tullessaan ensimmäistä kertaa sovellukseen käyttäjä painaa esim. "Sign in with Google" -nappia. Tällöin avautuu Googlen kirjautumisikkuna jossa käyttäjä antaa Google-tunnarinsa Googlen palvelimella sijaitsevalle sovelluksen login-applikaatiolle. Nyt käyttäjä on antanut sovellukselle oikeudet käyttää tunnistamiseen jatkossa login-applikaatiota. Tämän jälkeen loggautuminen sovellukseen voi tapahtua ainostaan yhdellä napinpainalluksella: "Login with Google". Tunnareita ei enää kysytä jos käyttäjän Google-kirjautuminen on selaimessa voimassa.
Seuraavassa kolme esimerkkiä Google-kirjautumisesta backend-puolella. Kaikissa käytetään Passport-kirjastoa. Enimmäinen on mahdollisimman yksinkertainen, ja se sisältää ohjeen login-applikaation luontiin Google Cloud -alustalle. Toinen hakee Googlelta saadut käyttäjäprofiilitiedot web-sivulle. Kolmas tallentaa käyttäjäprofiilitiedot MongoDb-kantaan. Kantaan tallennus ei ole välttämätöntä, mutta voi olla jossain tapauksessa hyödyllistä tallentaa tieto siitä ketkä ovat sovelluksen käyttäjät. Backend-sovelluksen suojatuilla sivuilla liikkuminen voidaan toteuttaa esim. lisäämällä sessioon tai itse tehtyyn JWT-tokeniin Googlen datasta saatu käyttäjätieto joka tarkistetaan suojatun sivun reitissä.
-Express-passport-google-1
- Yksinkertainen Google-kirjautuminen passportin avulla
-Express-passport-google-2
- Google-kirjautuminen jossa haetaan käyttäjän
Google-profiilitiedot
-Express-passport-google-3
- Sama kuin edellä, mutta tallennetaan profiilitiedot kantaan
Edellisessä luvussa oli esitetty sosiaalinen login backend-sovelluksessa, jossa käyttöliittymänä oli backendin view. Sosiaalisen loginin toteutus full stack -sovellukseen vaatii Google-kirjautumisen myös frontend-sovellukseen. Frontend voi käyttää aiemmin backendiä varten tekemääsi Googlen login-applikaatiota, jos lisäät siihen frontendin URI-osoitteet. Jos haluat, voit luoda frontendille myös oman login-applikaation, login-applikaation luontiohje.
Google-kirjautuminen frontend-sovellukseen on esitetty seuraavissa esimerkeissä:
-JS Google signin
- Google-kirjautuminen vanilla-JS -frontista (ei sovelluskehystä)
-ang-googlesignin -
Google-kirjautuminen Angular-frontista.
Frontendistä otetaan yhteys login-applikaatioon joka palauttaa kirjautumisen onnistuttua käyttäjän profiilitiedot ja Googlen Id tokenin. Googlen Id token lähetetään frontendistä backendiin, joka käy tunnistamassa käyttäjän login-applikaatiossa sen avulla, ja käyttäjän profiili haetaan backendiin. Profiili voidaan tallentaa omaan tietokantaan, mutta se ei ole välttämätöntä. Backendissa profiilitiedoista luodaan uusi JWT-token joka viedään frontendiin selaimen sessionstorageen. Sitä käytetään pääsyyn SPA-käyttöliittymän "salaisille sivuille" ja pääsyyn REST-apin suojattuihin reitteihin.
Seuraavassa esimerkissä on toteutettu edellä esitetty full stack -kirjautumisprosessi. Esimerkissä on Google-kirjautumisen lisäksi tavallinen kirjautuminen omassa tietokannassa olevilla tunnareilla (vrt. tehtävä 6). Google-kirjautuminen ei tarvitse tietokantaa, koska Googlelta saatavaa dataa ei tässä tapauksessa tallenneta tietokantaan.
-Full stack Google-kirjautumisen esimerkki (MEAN-stack)
Nestjs on Expressiin perustuva, mutta sitä pidemmälle kehitetty, enterprise-tasoisten sovellusten kehittämiseen tarkoitettu Nodejs backend-sovelluskehys. Nest on Typescript-sovelluskehys, ja sen arkkitehtuuriratkaisut on kopioitu suoraan Angular frontend-sovelluskehyksestä. Angularin tavoin Nest pakottaa kehittäjän käyttämään määrättyjä arkkitehtuuriratkaisuja, jolloin ei ole mahdollista toteuttaa erityisen huonoa arkkitehtuuria. Nestin arkkitehtuuri muistuttaa myös paljon .NET(C#) ja JEE(Java) -backend-sovelluskehysten arkkitehtuuria. Nestin suosio on viime vuosina lisääntynyt varsinkin sen vuoksi, että se soveltuu hyvin mikropalveluiden kehittämiseen.
Nestjs-sovellus muodostuu moduuleista, jotka ovat
päämoduulin (app-module) sisällä. Moduulin tärkeimmät rakenneosat
ovat kontrolleri, joka
ottaa vastaan palvelimelle tulevia pyyntöjä ja tarjoaa
asiakassovellukselle vastauksia pyyntöihin, sekä service, joka tarjoaa
palveluita kontrollerille, esim. toimittaa kontrollerille datan,
jonka kontrolleri välittää vastauksena asiakkaalle. Moduuli voi
sisältää edellisten lisäksi muitakin rakenneosia, kuten middleware, pipe, guard ja interceptor. Nest-sovellus käynnistyy
main.ts -tiedostosta.
Peruskomennot sovelluksen rakentamiseen:
Uusi moduuli ja sen kontrolleri ja service menevät omaan kansioonsa, jonka nimi on sama kuin moduulin nimi. Uusi moduuli sijoitetaan AppModulen imports-taulukkoon. Kun ajat sovelluksen npm start -komennolla, se samalla buildataan dist-kansioon. Kansioon syntyy js-tiedostojen lisäksi myös d.ts-päätteisiä deklaraatiotiedostoja. Niitä tarvitaan vain jos sovellus julkaistaan npm-moduulina julkisessa pakettivarastossa kuten npmjs.com:ssa. Muussa tapauksessa ne voi poistaa.
Tyypillisimpien palvelinpuolen sovellusten, kuten esim. REST-API:n kehittäminen Nestillä ei ole kovinkaan erilaista verrattuna samanlaisten sovellusten kehittämiseen Expressillä. Suurimman eron aiheuttaa Nestin määrättyihin rakenneosiin perustuva arkkitehtuuri ja Typescriptin käyttö.
-Nestjs, MongoDb, Mongoose
REST-API -tutoriaali
Mikropalveluarkkitehtuuri tarkoittaa sitä, että backend muodostuu useammista pienistä backend-sovelluksista eli mikropalveluista. Mikropalvelut voivat välittää dataa toisilleen tai asiakassovellukselle, joko suoraan tai gatewayn kautta. Gateway, eli portti tai yhdyskäytävä, on palvelimen osa, joka kokoaa mikropalveluiden "endpointit" yhteen ja sen kautta pääsee käsiksi kaikkiin sovelluksen mikropalveluihin. Mikropalveluilla on yleensä omat tietovarastonsa. Mikropalvelu, jolla ei ole omaa tietovarastoa voi toimia tiedon suodattajana tai käsittelijänä toimittaen muokatun tiedon eteenpäin gatewaylle, toiselle mikropalvelulle tai asiakkaalle.
Mikropalvelusovelluksen vastakohta on monoliittinen sovellus, jossa kaikki palvelut ovat kiinni samassa backend-sovelluksessa ilman mahdollisuutta erottaa niitä toisistaan. Mikropalvelusovellus ei ole automaattisesti parempi ratkaisu kuin monoliittinen sovellus, vaan sopivin arkkitehtuuri pitää valita tilanteen mukaan. Mikropalveluarkkitehtuurin etuja ovat mm. mahdollisuus päivittää sovellusta nopeasti uudella mikropalvelulla ja mahdollisuus päivittää vain tietty mikropalvelu ilman että tarvitsee tehdä muutoksia koko sovelluksen koodiin. Eri kehittäjäryhmät voivat kehittää eri mikropalveluita eri teknologioilla, mikä tuo joustavuutta sovelluskehitykseen.
Nestjs sisältää erittäin hyvät välineet mikropalveluiden
kehitykseen. Mikropalvelun luonti on nopeaa ja helppoa
@Nest/microservices-kirjastomoduulin avulla.
Nest-mikropalvelusovelluksissa mikropalvelut voivat viestiä
monilla erilaisilla protokollilla. Tyypillisiä
viestinvälitysprotokollia ovat esim. TCP, Redis pub/sub, MQTT jne. Kun
mikropalvelu luodaan, määritetään viestiprotokolla
transport-ominaisuuden arvona. Nest-mikropalvelut eivät yleensä
käytä HTTP-protokollaa, vaan ne keskustelevat asiakassovelluksen
kanssa gatewayn välityksellä. Mikropalvelut viestivät gatewayn ja
muiden mikropalveluiden kanssa esim. TCP-protokollalla, ja gateway
voi viestiä asiakassovelluksen kanssa HTTP-protokollalla eli
REST-API:n kautta.
Kun mikropalvelut julkaistaan pilvipalvelualustalla, jokainen mikropalvelu sijaitsee yleensä omassa Docker-kontissaan. Konttien hallinta tapahtuu Kubernetes-konttienhallintajärjestelmän avulla. Mikropalveluiden julkaisu pilvialustalla ei kuulu tämän kurssin sisältöön, vaan backendin rakentamista pilvipalvelualustalle käsitellään kurssilla backend-sovelluskehitys2.
-Building a microservice
in NestJS
-Build an API Gateway with
NestJS in 10 minutes
-NestJS: Microservices with gRPC, API Gateway,
and Authentication
-Yksinkertainen Nestjs-mikropalveluesimerkki
Websocket on protokolla joka toimii TCP-protokollan päällä. Se mahdollistaa reaaliaikaisen asynkronisen kaksisuuntaisen kommunikaation selainsovellusten (frontend) ja palvelimen (backend) välillä. Muodostuneessa yhteydessä osapuolet voivat lähettää ja vastaanottaa dataa samanaikaisesti (full-duplex). Websocket on paljon kevyempi ratkaisu reaaliaikaiseen full-duplex kommunikaatioon kuin HTTP-protokollan päällä toimivat ratkaisut (XHR ja REST). Esimerkiksi selaimessa toimivia monen pelaajan pelejä on mahdollista toteuttaa websocketin avulla. Selaimen Websocket API on websocketin toteutus asiakaspuolella. Palvelinpuolelle on monia kirjastoja eri ohjelmointikielillä.
Socket.io (Socket.io wikipedia) on yleisin JS-kirjasto websocket-yhteyden toteuttamiseksi. Socket.io sisältää sekä palvelinpuolen että asiakaspuolen toteutuksen. Socket.io voidaan asentaa npm-moduulina. Socket.io ei ole puhdas websocket-kirjasto vaan se luo myös oman protokollan. Jos selaimessa ei ole websocket-tukea, Socket.io:ssa on sen varalta vararatkaisu.
Socket.io:n toiminta perustuu eventteihin, joita asiakas ja palvelin lähettävät ja vastaanottavat. Sovelluksen käyttäjälle luodaan asiakaspuolen socket-olio ja palvelimelle luodaan palvelinpuolen socket-olio. Socket-oliot emitoivat eventtejä asiakassovelluksesta palvelimelle ja toisinpäin. Palvelin suorittaa tavallisesti broadcast-lähetyksen eli emitoi viestin kaikille siihen kytkeytyneille asiakkaille. Pienessä Socket.io-sovelluksessa saattaa olla vain kaksi tiedostoa: server.js joka sisältää sekä http-palvelimen että socket-palvelimen, sekä client.html (kts. alla oleva chat). Isommissa sovelluksissa voivat http-serveri ja socket-serveri olla omissa tiedostoissaan ja client-puolella voi olla useampia tiedostoja.
- Yksinkertainen chat Socket.io:lla toteutettuna
- Using Socket.io to Create a Multiplayer Game
with Angular and Node.js
Nodejs-sovelluksia ei voi julkaista perinteisessä webbihotellissa jossa on yleensä tuki pelkästään PHP-palvelinohjelmoinnille. Yleensä käytetään erilaisia pilvihostauspalveluja (PaaS eli Platform as a Service) joissa käyttäjä voi valita palvelinalustan lukuisista eri vaihtoehdoista kuten Nodejs, Python, JavaEE/Tomcat, PHP/NGinx yms. Suurimmat pilvihostauspalvelut tarjoavat myös IaaS-palvelun jolloin voi pystyttää koko virtuaalikoneen haluamallaan käyttöjärjestelmällä varustettuna.
Pilvihostauspalvelut mahdollistavat sovelluksen helpon ja nopean skaalautuvuuden. Tyypillisesti tarjotaan 1-3 ilmaista sovellusalustaa. Näitä voi skaalata lennossa lisäämällä prosessoreja, muistia, levytilaa yms. Skaalaus on maksullista, mutta ilmainen sovellusalusta sopii hyvin sovellusten testialustaksi ja kevyeen käyttöön.
Yrityksissä sovellusten julkaisu sisältyy nykyään DevOps -prosessiin, johon perehtyminen tarkemmin vaatii käytännössä oman kurssin. Pilvihostauspalvelut sisältävät omia työkalujaan DevOps -prosessin toteutukseen. Julkaisu voidaan tehdä Docker-kontissa, jolloin toimintaongelmia aiheuttavat epäyhteensopivuudet kehitysympäristön ja julkaisuympäristön välillä poistuvat ja hyvin toimivan DevOps -prosessin toteutus helpottuu. Tästä seuraava askel on sovelluksen julkaisu serverless-alustalle, jolloin sovelluksen infra voidaan unohtaa lähes kokonaan.
Tunnettuja suuria pilvihostauspalveluja ovat mm. AWS, Microsoft Azure, Google Cloud, Heroku, Netlify, Jelastic ja Openshift. Suurimmat pilvipalvelut vaativat yleensä käyttäjältä luottokorttitiedot vaikka tarkoituksena olisi vain kokeilla ilmaista palvelua. Korkeakouluopiskelijoille on kehitetty luottokortittomia vaihtoehtoja.
***