NodeJS
Forutsetninger:
Takket være Node.js, så kan vi bruke JavaScript også som et språk på server-siden.
Akkurat som nettleseren har et sett med spesifikke ord og uttrykk har Node også sine.
Mens nettlesermiljø fokuserer på å endre nettsiden dynamisk, legge til knappelyttere osv, kan Node.js lytte til forespørsler og servere en respons, opprette filer og mapper på serveren, og mye mer.
Installasjon
Node
De aller fleste har en nettleser installert, men de færreste har nok Node.js installert.
Siden du har Windows Subsystem for Linux installert kan du ganske enkelt hoppe inn i kommandolinja og installere det der. Dollartegnet er bare for å vise at kommandoen skal skrives i kommandolinja.
$ sudo apt install nodejs npm
$ node -v
Når du trykker enter, etter å ha skrevet node -v, så vil terminalen skrive ut versjonsnummeret på node, dersom du fikk til å installere det. Hvis du ikke får opp et versjonsnummer kan du prøve å restarte maskinen. Hvis ikke det heller fungerer kan du prøve å kjøre igjennom installasjonen en gang til. I verste fall må du google “install node.js problem [det du opplever]”, og se om du finner en løsning. Legg spesielt merke til hvis du finner treff på stackoverflow.com – det er som regel bra svar.
Kommandolinja
Kommandolinja kan virke litt skummel hvis man ikke har brukt den noe særlig, men vi skal ta dette trinn for trinn. Vi kommer dessverre ikke utenom å bruke kommandolinja, hvis vi full-stack utviklere skal bli.
Vi kan nå starte node, og deretter kjøre javascript-kode direkte i terminalen:
$ node
> 2 + 2
Da får du tilbake tallet 4. Dette er veldig likt når vi kjørte javascript-kode i konsollet på browseren. La oss prøve et par ting til:
> let myFavoriteNumber = 10
undefined
> 7 + myFavoriteNumber
17
Hva om vi ønsker å bruke Node.js sin kraft, og ikke bare små en-linjes kodesnutter?
Da burde vi over i en tekst-editor, istedenfor terminalen. Derfor kan du i terminalen skrive:
> .exit
Vår første .js-fil
Lag deg en mappe. Det spiller ingen rolle hvor det er – det kan være på desktop, i dokumentmappa di, eller en egen programmeringsmappe du kan lage deg. Men lag en mappe som du kaller for first-node-experiment. Navnet spiller naturligvis ingen rolle, men kan være et prosjektnavn.
Hvis du allerede har visual studio code (vscode) kjørende, så finnes det flere måter å åpne en mappe på. Du kan
- Ta tak i mappa, og dra den inn i vscode, ELLER
- Du kan klikke file -> Open Folder, og deretter velge mappa du har laget, ELLER
- Du kan høyreklikke mappa og velge “Open in Code”
I menyen til venstre kan du nå opprette filer i mappa:
Hvis ikke du ser ikonene er det fordi musa må være over den linja hvor det står first-node-experiment. Du kan også trykke ctrl+n for å lage en ny fil, eller høyreklikke i menyen til venstre, og velge New file. Kall fila for test.js. Navnet på fila har ingen betydning, men det er viktig at du har en punktum og at det står js bak punkumet, slik at programmet forstår at dette er en javascript-fil.
På samme måte som i codepen, så kan vi nå skrive flere linjer med kode, i motsetning til i terminalvinduet.
let strawberryCount = 20
if (strawberryCount > 10) {
console.log("You have enough strawberries")
} else {
console.log("You do not have enough berries")
}
Hold inne ctrl og trykk s for å lagre (ctrl+s), eller gå til file -> save.
La oss nå sende denne fila inn i node, slik at det ikke er nettleseren, men Node som kjører programmet vårt. En grunn til at jeg foretrekker vscode over andre tekstbehandlere, er fordi at hvis vi nå holder inne ctrl og trykker ø, så får vi opp en terminal inne i vscode. Du kan også gå til View og velge Terminal. Dette er veldig komfortabelt. Terminalen i vscode går automatisk til den mappa der prosjektet vårt ligger. Hadde vi brukt en terminal utenfor vscode, hadde vi måtte gå til terminalen, og navigert oss frem til mappa, ved bruk av cd (change directory).
I terminalen i vscode kan vi nå skrive node + navnet på fila som vi vil kjøre med node
$ node test.js
Teknisk sett trenger vi ikke .js når vi kjører node program, så du kan heretter droppe .js når du kjører programmet.
Nå fikk du tilbake “You have enough strawberries”. Hvis du endrer strawberryCount til et lavere tall, som f.eks 3, og kjører programmet en gang til:
$ node test
Så vil du få opp “You do not have enough berries”.
Med dette har vi nok til å kunne sette opp vår første node.js server, som er det neste vi skal se på
Vår første server
Vi skal nå lage en liten server, som tar inn data fra et skjema, og behandler det.
For å illustrere hva vi skal lage:
What is the color on a bright and sunny day?
[ input-field ] submit answer [button]
Hvis brukeren skriver noe annet enn “blue”, så får de én beskjed, og hvis de skriver inn “blue”, så får de en annen beskjed. Dette virker kanskje ikke superavansert, men det er en utrolig viktig brikke i det å kunne lage applikasjoner. Dette kunne jo likegjerne vært et passordfelt, som sjekker at brukeren taster inn riktig passord.
La oss først fjerne alt som er i test.js, så vi kan begynne med et rent ark – bokstavlig talt.
Det vi ønsker å oppnå er å lage en server, som lytter til forespørsler fra nettleseren vår, slik at når vi besøker nettsiden med nettleseren, så får vi tilbake en respons fra serveren, som f.eks “hello world” eller “velkommen til min nettside”.
For å bruke node må vi bruke miljø-prat, som er spesifikt for node.js.
Skriv http i fila. VSCode vil foreslå mange forskjellige ord som vi ikke skal bruke nå. For å unngå at vscode skriver inn forslaget, trykk Escape-tasten. Skriv et punktum og createServer()
http.createServer()
http er ikke en del av javascript-språket, men det er en del av nodejs, på samme måte som document var en del av nettleser-miljøet, men ikke av javascript.
La oss tilegne linja vi nettopp skreiv, til en variabel:
let ourApp = http.createServer()
ourApp.listen()
ourApp har da en metode som heter listen som vil lytte til hva enn vi definerer i ourApp.
createServer() er en metode som er en funksjon av en høyere orden; den tar altså inn en funksjon som et argument. Vi kunne ha laget en funksjon som vi deretter kaller som et argument, men siden den funksjonen i såfall aldri skulle ha blitt kjørt noe mer, så kan vi like gjerne lage en anonym funksjon:
let ourApp = http.createServer(function(req, res) {
})
ourApp.listen()
req er kort for request, men du kan i teorien kalle den hva du vil – req er bare en konvensjon, noe de aller fleste skriver.
res er kort for response, og du kan igjen egentlig kalle den hva du vil, men det er konvensjon å kalle den for res.
Så, i kroppen til funksjonen legger vi til kode som kjøres når noen går inn på siden:
res.end("Hello and welcome to our website")
Et par ting før vi lagrer og tester koden vår. Helt øverst i dokumentet må vi importere http-funksjonen. Selvom http-funksjonen er en del av node, så må vi importere den for å kunne bruke den. Så på første linja av dokumentet, skriv denne linja:
let http = require("http")
Som argument i ourApp.listen(), så må vi ta inn hvilket portnummer vi skal lytte til. Hvis ikke du vet hva et portnummer er, så gjør det ingenting – vi kommer tilbake til det senere. Sett argumentet til 3000 i listen-metoden:
ourApp.listen(3000)
Og vi står da til slutt igjen med:
let http = require("http")
let ourApp = http.createServer(function(req, res) {
res.end("Hello and welcome to our website")
})
ourApp.listen(3000)
La oss nå lagre dokumentet, og gå tilbake til terminalen, og kjøre
$ node test
Nå kjører serveren vår i terminalen. Gå til nettleseren, og fjern det som står i adressefeltet. Skriv inn localhost:3000. Trykk enter, og du vil nå komme inn på serveren vi har laget. Jippi!
Localhost er vår maskin, og ingen andre kan se nettsiden. 3000 er porten som vi valgte.
Hva om vi ønsker å ha en underside, slik som localhost:3000/about?
Vi kan gjøre slik at vi sender en type innhold når brukeren går til localhost:3000, og et annet type innhold når brukeren går til localhost:3000/about.
Vi har allerede testet å bruke response-objektet (res), men la oss nå se hva vi kan gjøre med request-objektet (req). Req inneholder mengder med info om forespørselen som blir sendt inn, inkludert hvilken URL som har blitt etterspurt – i vårt tilfelle localhost:3000/about.
Rett over res.end()-linja vår, kan vi skrive følgende:
console.log(req.url)
Lagre fila, og kjør serveren igjen:
$ node test
Tilbake i nettleseren, gå til localhost:3000/about
Hvis du ser i terminalen, vil du nå se at /about ble logget inn. Du kan teste andre adresser også, f.eks localhost:3000/pizza, og du vil se at det logges ut i terminalen. Hvis du går til localhost:3000, så vil den logge ut /, som viser at det er rotområdet på serveren.
La oss fjerne console.log-linja igjen, og klipp ut res.end()-linja.
Skriv følgende:
if (req.url == "/") {
// kopier inn linja her, og gjør om teksten litt:
res.send("Hello, and welcome to our home page")
}
if (req.url == "/about") {
res.send("Thank you for the interest in our company")
}
Lagre fila, stop serveren ved å holde inne ctrl og trykke c, og deretter start den opp igjen, ved å trykke pil-opp, og så enter. Hvis du nå går til de to forskjellige adressene localhost:3000 og localhost:3000/about, vil du se at vi sender forskjellig innhold. Fint!
Under if-setningene kan du legge til
res.end("We cannot find the page you are looking for")
Denne linja gjør at dersom brukeren prøver en annen side enn rotområdet (localhost:3000) eller localhost:3000/about, så blir de sendt til den siden. Du kan prøve dette ved å lagre fila igjen, stoppe serveren med ctrl+c, og deretter trykke pil-opp og så enter, for å kjøre serveren igjen.
Hvis du nå tester de to URL’ene, og en URL du vet at ikke fungerer, så får du testet alle tre.
Nå har du satt opp din aller første, superenkle server.
Express
Nå skal vi prøve å ta imot informasjon fra et input-felt, og håndtere den informasjonen fra serveren.
Vi skal bruke et bibliotek som heter Express. Stort sett styrer vi unna å bruke rammeverk og bibliotek som hindrer oss i å få en grunnleggende forståelse av hva som foregår “under panseret”, eller gjør alle operasjoner automagisk for oss uten at vi forstår hvorfor, eller hvordan.
Express gir oss derimot bare et sett med funksjoner som er minimalistisk, uten å påtvinge oss en spesifikk måte å gjøre ting på, og gjør heller ikke for mye automatisk – vi må selv forstå hva som foregår. For å si litt om hvor populært Express er, så har det blitt nedlastet 5.000.000 ganger, bare denne uken.
For å laste ned og installere Express, så går vi til terminalen i vscode. Her skriver vi følgende:
$ npm install express
Hva skjer her? Når vi installerte Node.js på datamaskinen, følger det med noe som heter npm, som står for Node Package Manager. Det er et program som kan laste ned og installere pakker (program) på maskinen vår, ved å skrive inn kommandoer i terminalen. Ordet install ber npm om å installere noe, og noe i vårt tilfelle er express.
Når den er ferdig med å installere, vil du se at det har blitt opprettet en ny mappe som heter node_modules. Hvis vi kikker i mappa, og scroller ned til bokstaven e, vil vi finne en mappe for express. Så kan man lure på hvorfor alle de andre mappene er der? De er der, fordi express er avhengig av de andre pakkene for å kunne fungere, så når vi lastet ned express, så tok den også med alle pakker som den trenger. Det er sjelden noen grunn til å åpne node_modules mappa, men nå vet du iallefall hva som ligger der.
Det du installerer med npm install blir installert til den mappa som terminalen står i. For min del åpnet jeg mappa first-node-experiment med vscode, og deretter åpnet en terminal, og da står terminalen automatisk i riktig mappe. Nok om det – la oss hoppe over i test.js og skrive litt kode igjen. Begynn med å fjerne alt av tekst som er i test.js. På samme måte som vi inkluderte http, må vi inkludere express:
let express = require("express")
Deretter gjør vi noe lignende det vi gjorde med http-metoden:
let ourApp = express()
Og på linja under, få serveren til å lytte til en port:
ourApp.listen(3000)
Over lyttelinja kan vi skrive:
ourApp.get(a, b)
- URL vi ser etter. La oss sette den til ‘/’, altså rotområdet vårt
- Hva som skjer når noen kommer inn på adressen vi definerte som a-argumentet. En funksjon
ourApp.get('/', function(req, res) {
res.send(`
`)
})
Vi tar nå inn en funksjon på samme måte som med http, som har et request-objekt, og et response-objekt. Med response-objektet kan vi sende tilbake informasjon til brukeren. Vi bruker back-ticks for å lage HTML. Inne i back-tick’ene kan du skrive:
<form>
<p>What color is the sky on a clear and sunny day?</p>
<input type="text" name="skyColor" autocomplete="off">
<button>Submit Answer</button>
</form>
La oss legge til noen attributter på form-tag’en:
<form action="/answer" method="POST">
La oss nå lagre fila, og kjøre serveren igjen:
$ node test
Gå til localhost:3000, så vil du få opp skjemaet. La oss prøve å fylle ut noe tekst, og trykke knappen. Da skjer noe bra, og noe kjipt: URL’en har endret seg til /answer, og det er bra – det er nettopp det vi ønsket. Men vi får opp en feilmelding på skjermen, hvor det står Cannot POST /answer. Vi har ikke laget en URL som heter /answer enda, og det er derfor vi får denne feilmeldingen. Det dekker hva action=”/answer” gjør. Det er ikke noe spesielt med navnet /answer – det kunne ha vært hva som helst. Method-attributtet derimot, er litt annerledes. Det finnes ferdigdefinerte måter å sende inn et skjema til serveren. Det finnes flere metoder man kan bruke, men de to vanligste er GET requests, og POST requests. Hvis du går til www.google.com, så sender nettleseren din en GET request. Samme hvis du trykker en link på google.com. Så, hvis du surfer rundt på nettet fra link til link, så sender nettleseren bare GET-requests. Men når vi går tilbake til localhost:3000, så trenger vi ikke bare å få data, men vi skal sende data tilbake, og det er da vi bruker en POST-request. La oss gjøre litt testing for å få en god forståelse av forskjellen på GET og POST:
Tilbake i koden vår har vi hittil følgende kode:
let express = require('express')
let ourApp = express()
ourApp.get('/', function(req, res) {
res.send(`
<form action="/answer" method="POST">
<p>What color is the sky on a clear and sunny day?</p>
<input type="text" name="skyColor" autocomplete="off">
<button>Submit Answer</button>
</form
`)
})
Nå gir det kanskje litt mer mening med hvorfor vi har ourApp.get(…). Det er fordi vi lager en GET URL for serveren vår. Slik at når brukeren prøver navigere til localhost:3000, gjøres det en GET-request.
Under ourApp.get() kan vi lage en ny URL som er en POST-metode:
ourApp.post('/answer', function(req, res) {
res.send('Thank you for submitting the form')
})
Kopier hele dette så du har det to ganger. På den ene kopien endrer du fra .post til .get, og endrer teksten litt:
ourApp.get('/answer', function(req, res) {
res.send('Are you lost? There is nothing to see here'.)
}
Lagre fila, og restart serveren igjen (gå til terminalen, ctrl+c, pil-opp, enter). Deretter går du til nettleseren, og skriver localhost:3000 igjen. Så fyller du ut skjemaet, og trykker på knappen. Da får du se POST-responsen. Hvis du går tilbake til localhost:3000, og så prøver du å skrive inn localhost:3000/answer, og trykke enter i adressefeltet, så vil du få GET-responsen.
Kortere fortalt: GET-requests skjer oftest når man navigerer seg rundt på internett – POST-requester skjer oftest i sammenheng med innsending av data med et skjema.
La oss gå tilbake til post-funksjonen for ‘/answer’. Fjern linja med res.send, og la oss lage en if:
if (x == "blue") {
} else {
}
X er i dette tilfelle bare en placeholder – en midlertidig verdi. Vi ønsker egentlig at x skal være verdien av det brukeren har fyllt inn i input-feltet, så hvordan får vi tak i det?
Det ligger i request-objektet (req), og vi kan hente det ut ved å skrive req.body.skyColor. skyColor er navnet som vi ga til input-feltet ved name-attributtet
if (req.body.skyColor == "blue") {
// riktig svar
res.send(`
<p>Congrats, that is the correct answer!</p>
<a href="/">Back to homepage</a>
`)
} else {
// galt svar
res.send(`
<p>Sorry, that is incorrect.</p>
<a href="/">Back to homepage</a>
`)
}
Som standard vil ikke Express la oss få tak i body-objektet i req-objektet, men det er et valg man må skru på. Rett under linja der vi starter express (let ourApp = express()), legg til denne linja:
ourApp.use(express.urlencoded({extended: false}))
Dette er en linje som ikke er nødvendig å pugge – har du bruk for den, kan du slå opp i dokumentasjonen til Express. Det eneste linja gjør er å gjøre det lettere for oss å få tak i brukerens input i request-objektet, slik som vi gjør over.
La oss lagre fila igjen, restarte serveren, og prøve å skrive inn et galt svar og trykke knappen, og deretter å skrive inn rett svar, og trykke knappen.
En siste ting til dette skjemaet: hvis noen skriver Blue eller BLUE, så bør det komme tilbake som rett svar. Vi kan gjøre at svaret ikke bryr seg om store eller små bokstaver, såkalt case insensitive. Alt vi trenger å gjøre for å oppnå dette er å endre if-linja til:
if (req.body.skyColor.toUpperCase() == "BLUE")
Så kan du lagre og restarte serveren, og teste med BLUE, blue, BLUe eller lignende.