Follow Us on Twitter

Node.js, server-side JavaScript

Gepubliceerd in

JavaScript heeft als programmeertaal de laatste jaren een enorme ontwikkeling doorgemaakt. Het is ontwikkeld in 1995 door Brendan Eich, een medewerker van Mozilla. De taal heette eerst Mocha, vervolgens LiveScript, en is datzelfde jaar nog hernoemd naar JavaScript. Daar zat ongetwijfeld marketing achter, en de wens om mee te liften op het succes van het toen net populair geworden Java. We hebben het dus over 1995; het jaar van Windows '95, de film Braveheart en de oprichting van Amazon.com. Die website zag er toen zo uit:

Amazon website in 1995

Een van de belangrijkste recente ontwikkelingen is dat JavaScript niet meer alleen als scripttaal in de browser gebruikt wordt, maar ook aan de serverzijde. De code achter webservices en webservers kan dus geschreven zijn in JavaScript. En dat maakt JavaScript plotsklaps een meer relevante taal voor elke developer! Logo Node.jsAlhoewel er meer serverside applicaties met JavaScript bestaan, is met de komst van Node.JS in 2009 er een belangrijke speler op de markt bij gekomen. Eentje die onlosmakelijk met JavaScript is verbonden, een eigenzinnige architectuur heeft en die erg modulair van opzet is met een ingebouwde package manager.

In dit Whitebook bekijken we de taal JavaScript en de architectuur van Node.js, en laten we aan de hand van een eenvoudige website zien hoe Node.js werkt. We stellen de vraag waar we Node.js in kunnen zetten en of de beloofde modulariteit en schaalbaarheid waar te maken zijn.

Introductie tot JavaScript

Er bestaan veel misverstanden rond JavaScript. Vaak wordt gedacht dat het “een soort Java is”. JavaScript is, zoals de naam suggereert, een scripttaal. Het belangrijkste kenmerk van een scripttaal is dat het niet eenmalig gecompileerd wordt in een binaire executable, maar runtime op het moment dat het nodig is door een zogenaamde interpreter. Ook dan is het resultaat machinecode, net zoals bij een compiler.

Alhoewel Java en JavaScript in naam en syntax sterke overeenkomsten vertonen, is een belangrijk verschil dat JavaScript in de kern geen OO-taal is. Het ondersteunt wel alle belangrijke Object Orientatie principes, zoals information hiding, inheritance en het gebruik van objects. JavaScript is vooral een functionele taal. De concepten hierachter zijn academisch en bijna wiskundig, maar het komt erop neer dat functies first-class moeten zijn. Dit houdt in dat ze als argument aan een andere functie meegegeven kunnen worden of als het resultaat van een functie teruggegeven kunnen worden. Dat resulteert erin dat de functies in een functionele taal ‘higher order’ kunnen zijn; deze functies krijgen een andere functie als argument mee. Pas in versie 8 van Java zijn een aantal van deze concepten aan Java toegevoegd.

Node.js principes

Node.js is een JavaScript-Runtime gebouwd op Chrome V8 JavaScript-engine. Het is specifiek ontwikkeld om serverside webapplicaties in te bouwen. De kracht van Node.js is dat het efficiënt, lichtgewicht en extreem schaalbaar is. Het is ook een flexibel platform. Je hebt de keuze om hele HTML-pagina’s uit te serveren, om pagina’s te genereren met JavaScript, AJAX- en RESTcalls af te handelen, API’s te bouwen of een combinatie van dat alles. Omdat een groot deel van de clientside van moderne websites al in JavaScript is geschreven (denk hierbij aan jQuery, Angular, Oracle JET en React), is de stap klein om de serverside in dezelfde taal te schrijven. Een bijkomend voordeel is dat delen van de bestaande JavaScript code naar de backend verplaatst kunnen worden en niet meer met de pagina meegestuurd hoeven worden naar de client. Denk hierbij aan functies die businesslogica bevatten, die kunnen zo eenvoudig “verservicet” worden.

Ook is Node.js extreem modulair. Out of the box is het uitgeklede server engine, en het kan naar wens uitgebreid worden met eigen code of, waarom niet, bestaande code. Daarvoor is de package manager npm meegeleverd; deze opent eenvoudig de weg naar integratie van MySQL, MongoDB (mongoose), authenticatie (passport), maar ook wrappers voor Java en zelfs een ingebouwde PHP-server.

De architectuur

De JavaScript engine is bijzonder snel; sneller dan andere bekende interpreters zoals SpiderMonkey en Nitro, en ook PHP, Ruby en Python worden voorbij gestreefd in performance. Er zijn voorspellingen dat de engine uiteindelijk net zo snel als C kan worden.

Node.js bestaat uit 2 onderdelen: de Core en de Modules. De Core is de Chrome V8 JavaScript engine. Deze zogenaamde JIT-compiler (van Just In Time) compileert de JavaScript code direct naar assembly code. Ook bevat hij een ingebouwde profiler die, waar nodig, optimalisaties doorvoert in de code. Ook garbage collection wordt door de core voor zijn rekening genomen.

De modules bestaan uit een enkele C++ library met de naam Libuv. Deze neemt alle asynchrone I/O en de zogenaamde "main event loop" op zich, zodat de V8 engine alle andere requests kan verwerken. Libuv is ontstaan uit twee andere bibliotheken, Libio en Libev. Door het deels ontbreken van Windows-ondersteuning en de steeds grotere overhead is in v0.90 van Node.js gekozen voor het herschrijven van de twee bibliotheken in één nieuwe.

Architectuur Node.js

Node.js is, tegen de verwachting in, single threaded. Alhoewel dit suggereert dat er geen concurrency mogelijk is, weet Node.js dit toch te realiseren met een concept dat “events en callbacks” heet. Zodra de applicatie start, initialiseert Node.js de variabelen en de functies, en wacht daarna simpelweg tot er een event plaatsvindt. De event loop neemt deze taak op zich, en draagt de afhandeling van het event af aan de event handlers.

Node.js Eventloop

Aan de hand van een praktijkvoorbeeld wordt dit wat concreter.

Maken van een webserver met Node.js

Met het starten van een Node.js programma hebben we nog geen server!

De basis van elke Node.js is een JavaScript bestand. Het bestand main.js (de naam is arbitrair, maar ik gebruik in de voorbeelden main.js) bevat de code voor het starten van de http server, en het commando node main.js start de node applicatie.

De applicatieserver moet de programmeur zelf implementeren in het Node.js programma. Dat doe je met een paar regels JavaScript code die ieder Node.js programma op één of andere manier zal bevatten:

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');	
}).listen(3000, "127.0.0.1");
console.log('Server running at http://127.0.0.1:3000/');

De zojuist aangemaakte HTTP server heeft een callback naar request event. De callback wordt op zijn beurt als argument naar de createServer gestuurd (Het codeblock toont een voorbeeld van een higher order functie. Deze manier van code schrijven is voor beginnende JavaScript programmeurs een uitdaging om onder de knie te krijgen, en voor gevorderden vaak ook nog).

Aan het createServer programma wordt een event listener vastgemaakt. Dit heeft als gevolg dat node.js niet stopt na het uitvoeren van de code, maar blijft wachten totdat er een event afgaat. Wanneer de server een HTTP request ontvangt, gaat Node.js naar het request event dat de callback genereert, en de bijbehorende code wordt uitgevoerd.

Hoe ziet dat er voor de eindgebruiker uit? De eerste regel van bovenstaande code is een soort import van de HTTP library. Daar kom ik in de volgende paragraaf uitgebreider op terug. De regels 2 t/m 5 maken de server aan en bevatten de callback code. De code tussen brackets wordt nog niet meteen uitgevoerd! De laatste regel schrijft een logregel naar de console. In de praktijk is dat de stdout van de commandline waar de node.js server gestart is.

MacBook-Air:demonode patricksinke$ node main.js &
[1] 1317
MacBook-Air:demonode patricksinke$ Server running at http://127.0.0.1:3000/
	
MacBook-Air:demonode patricksinke$ 

Hier zie je de logging naar de console geschreven worden, direct nadat de server gestart is. Normaal zou je verwachten dat nu de laatste regel code uitgevoerd is, het programma beëindigd is, maar dat is niet het geval! Op dit moment is de event loop aan het werk en wacht op events die op port 3000 van de server binnenkomen. Er gebeurt verder helemaal niets, totdat iemand besluit zijn browser naar port 3000 op het IP adres van de server te sturen.

Hello World

Voila, een webpagina. Daar hebben die twee regels tussen accolades voor gezorgd. Het event van een HTTP verzoek op port 3000 van de server was de trigger om de callback uit te voeren, en dat was het moment waarop de code uitgevoerd werd en een eenvoudige webpagina gegeneerd werd en teruggegeven aan de requestor. Daarmee hebben we de basis te pakken van hoe node.js werkt.

Ontwikkelen in Node.js

Niemand zit vanzelfsprekend te wachten op een webserver die alleen met “Hello World” kan antwoorden. We willen een serieuze webapplicatie bouwen met een mooie layout, formulieren, asynchrone servercalls naar een database en allerlei toegevoegde functionaliteit. Daar zullen we wat extra werk voor moeten doen, maar met een beetje slimmigheid, hoeft dat niet eens zoveel werk te zijn.

Die slimmigheid zit ‘m natuurlijk in alle bibliotheken en tools die tot de beschikking van de programmeur staan. Aan de frontend helpen libaries zoals Font Awesome en jQuery ons om een consistente en rijke UI te creëren. Daar verandert eigenlijk niets. Wat wel verandert is de manier waarop de pagina uitgeserveerd wordt en hoe de functionaliteit aan de serverzijde gerealiseerd wordt.

Hoe gaan we nu verder? Zoals gezegd gebruiken we de package manager npm om modules toe te voegen. Met dit commandline programma doen we dat in de vorm van npm install <librarynaam> -g. De –g switch is optioneel; als je deze meegeeft wordt de module globaal geïnstalleerd, dus voor alle projecten. Zonder –g installeert hij alleen in het huidige project.

Het meest gebruikte framework voor het bouwen van webapplicaties in Node.js is Express. Het neemt veel werk uit handen van de programmeur voor het gebruik van routing, RESTful, template-engines, feitelijk de hele middleware van een webapplicatie. Een onderdeel van Express is het standaardgebruik van Jade, waar ik persoonlijk niet de voordelen van inzie. Het is een template engine voor HTML, geïnspireerd op Ruby en HAML en met een Python-achtige syntax. Liever schrijf ik gewoon zelf HTML of gebruik ik EJS (Embedded JavaScript) om HTML te genereren.

Terug naar onze Node.js applicatie. Ik noemde al eerder het require keyword. Dit heeft sterke gelijkenissen met het import keyword in C om externe bibliotheken aan de code toe te voegen. In het voorbeeld wordt ook een require van ‘http’ gedaan die webservermogelijkheden aan de applicatie geeft. Als we Express met npm installeren, kunnen we die ook gebruiken. Onze webserver wordt dan nog simpeler:

var express = require('express');
var app = express();
app.get('/', function(req, res){
  res.send('Hello Express');
});
app.listen(3000);

Hier is ook te zien dat de modules first class zijn; ze (‘express()’) worden aan een variabele (‘app’) toegekend.

Laten we voor de eenvoud uit gaan van een standaard website met losse CSS en JavaScriptbestanden. De directorystructuur van het Node.js project ziet er bijvoorbeeld zo uit:

Mappenstructuur Node.js app

In de directory nodejs-app staan de Node.js sources (zoals package.json en main.js). In de public directory plaatsen we de website sources (zoals index.html, css-bestanden en afbeeldingen).

Voeg nog wel de volgende regel toe aan main.js om toegang toe te staan:

app.use(express.static('public'));

Een HTML-pagina in de public directory wordt op de volgende manier geserveerd:

app.get('/', function(req, res) { res.sendFile( '/index.html'); });

Als de eindgebruiker naar de website navigeert, bijvoorbeeld http://www.nodejsiscool.nl/, dan krijgt hij de index.html te zien. Stel dat in index.html een link naar somecode.js staat (die in de assets directory op de server staat), dan kan verwezen worden via de link http://nodejsiscool.nl/assets/somecode.js.

Op deze manier fungeert Express als een middlewarelaag. Met deze module, en de enorme door de community ontwikkelde collectie andere modules zijn de mogelijkheden eindeloos.

De kracht van Node.js is dat het niet alleen complete webpagina’s serveert of genereert. Ook bijvoorbeeld asynchrone calls door formulieren kunnen afgehandeld worden, en het kan ingezet worden als een RESTful API in combinatie met MongoDB. De MEAN stack (MongoDB, Express, Angular, Node.js) is een mooi voorbeeld van hoe het beste uit verschillende componenten samen komt. Het is mogelijk om specifeke onderdelen van de webserver door Node.js af te handelen, en dat te combineren met andere runtimes. Bijvoorbeeld om de website uit te breiden met informatie uit een onlangs geïntroduceerde NoSQL database.

Conclusie

Het is wel duidelijk dat Node.js een interessante technologie is om in overweging te nemen wanneer je een website backend gaat ontwikkelen of uitbreiden. De voordelen zijn evident; het platform is flexibel, snel en schaalbaar. In combinatie met bijvoorbeeld Express ontstaat een robuuste middlewarelaag. Het biedt de mogelijkheid om bestaande en nieuwe functionaliteit te verservicen en API’s neer te zetten die herbruikbaar zijn. Met de nieuwe ECMA specificaties is het een moderne taal met een bijzonder snelle interpreter. De architectuur is lichtgewicht en alhoewel het stof rond de discussie over de voor- en nadelen van een single-threaded event-based architectuur nog lang niet is neergedaald, lijkt het met de juiste voorzorgsmaatregelen behoorlijk goed te schalen.

Het gegeven dat Node.js nog volop in ontwikkeling is, maakt dat het een belofte voor de toekomst is. Maar het is ook een risico; de API is nog niet stabiel en het is makkelijk te verdwalen in de wirwar van modules. Zo heb ik een keer een uur gezocht naar een probleem met de jQuery module, voordat ik er achter kwam dat de door mij gebruikte "jQuery" was opgevolgd door "jquery" (ja, zonder hoofdletter). Daar komt nog bij dat er een reële kans is dat nieuwe versies van één module problemen veroorzaken.

Het lijkt erop dat deze problemen te overkomen zijn, aangezien grote sites zoals die van Walmart, Netflix, Paypal, Uber en LinkedIn in Node.js gebouwd zijn. De verwachting is niet dat Node.js “the one to rule them all” is, wel verdient het een plek op de shortlist van eenieder die op zoek is naar een geschikt webdevelopment-platform .

Referenties en aanbevolen materiaal

Waardering:
 
Tags:

Reacties

Nieuwe reactie inzenden

De inhoud van dit veld is privé en zal niet openbaar worden gemaakt.

Meer informatie over formaatmogelijkheden

CAPTCHA
Deze vraag is om te testen of u een persoon bent en om spam te voorkomen
Image CAPTCHA
Enter the characters shown in the image.