KMI/WEBA Webové aplikace: Seminář 10

Node.js

Doposud jsme spouštěli JavaScriptový (JS) kód výhradně ve webovém prohlížeči, který poskytuje pro JS běhové prostředí pro. Open-source technologie node.js poskytuje (alternativní) běhové prostředí (založené na V8) pro JS kód. Ten je díky tomu možné spouštět i mimo webový prohlížeč.

Pro úplnost dodejme, že prohlížeče mají různá běhová prostředí pro JS. V8 je prostředí z Google Chrome.

Instalátor node.js je k dispozici na oficiálních stránkách projektu.

Hello world aplikace

V následující části si vytvoříme jednoduchou aplikaci. Uvažme kód uložený v souboru Apj.js.

console.log("Hello world");

Kód spustíme příkazem níže.

node App.js

Základní vlastnosti

Node.js umožňuje spouštět programy zapsané v JS kódů. Oproti klasickému vykonávání JS v prohlížeči, které jsme již několikrát viděli, je zde několik rozdílů. Node.js JS kód neinterpretuje, ale provádí (JIT) kompilaci. Ne vše z ECMAScript je v node.js podporováno. Node.js podporuje ES6 moduly a CommonJS moduly.

Součástí node.js je rozsáhlé API, které značně usnadňuje vývoj aplikací. Kromě API je možné využívat i moduly z npm. Ty je třeba vždy doinstalovat pomocí nástroje npm.

Dodejme, že ve výchozím stavu je node.js vždy spouštěn v development módu. Pro finální produkci je třeba tento mód vypnout. Podívejme se nyní na několik klíčových vlastností node.js.

Jedno vlákno

Node.js běží v jednom neblokujícím vlákně a používá asynchronní událostní řízení. Tím je vynuceno adaptování funkcionálního paradigmatu.

// triviální webový server s počitadlem
const http = require('http');

var pocet = 0;
  
// na rozdíl od běžných webových serverů, zde budou jednotlivé spojení inkrementovat proměnou pocet 
http.createServer(function (req, res) {
  res.end(`${++pocet}`);
}).listen(3000);

Událostní řízení

Typicky se kód dělí na funkce, které jsou mezi sebou provázány pomocí callback. Běžně se používájí tři základní vzory: sekvenční, paralelní a omezený paralelní (kombinace sekvěčního a paralelního). Zde je nutné si uvědomit, že funkce se běžně vykonávají asynchroně.

Příklad níže ukazuje událostní řízení pomocí modulu events.

const EventEmitter = require('events');

const eventEmitter = new EventEmitter();

eventEmitter.on('start', number => {
  console.log(`started ${number}`);
});

eventEmitter.emit('start', 42);

// poznámka: off odstraní listener

Neblokující I/O

Veeškeré vstupně výstupní operace jsou neblokující.

const fs = require('fs');

fs.readFile('soubor.txt', function(err, data){
  console.log('A');
});

console.log('B');

// vypíše B a A

Dodejme, že je možné provádět operace i synchroně.

Webový server

Hlavní výhoda technologie node.js spočívá ve spojení webového a aplikačního serveru. Tím je možné dosáhnout velkého výkonu. Typicky se technologie node.js využívá pro tvorbu (škálovatelného) webového back-endu, přičemž je možné snadno vytvořit monolitickou i mikro architekturu.

Násleudjící kód vytváří webový server a získává veškeré (relevantní) informace z HTTP dotazu.

const http = require('http');

// request a response jsou stream
const server = http.createServer((request, response) => {
  const { headers, method, url } = request;
    
  let body = [];
  
  request.on('error', (err) => {
    console.error(err);
  });
    
  request.on('data', (chunk) => {
    body.push(chunk);
  });
    
  request.on('end', () => {
    body = Buffer.concat(body).toString();
  
    response.on('error', (err) => {
      console.error(err);
    });
  
    const responseBody = { headers, method, url, body };
      
    // HTTP status, hlavicka
    response.writeHead(200, {'Content-Type': 'application/json'})
    response.write(JSON.stringify(responseBody));
    response.end();
  });
});
  
server.listen(8080);

Tvorbu webového serveru usnadňuje modul express, který umožňuje snadnější zpracování HTTP požadavků a routování.

const express = require('express');
const bodyParser = require('body-parser');

const app = express();
const port = 3000;

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

// GET
app.get('/', (req, res) => {
  res.send(JSON.stringify(req.query));
});

// POST
app.post('/', (req, res) => {
  res.send(JSON.stringify(req.body));
});

app.listen(port, () => {
  console.log(`Listening on port ${port}`)
});

Komplexnější příklad využívající expres následuje.

const express = require('express');
const bodyParser = require('body-parser');

const app = express();
const port = 3000;


app.use(express.json());
app.use(express.urlencoded({ extended: true }));


// log
const log = function(req, res, next) {
  console.log(`${req.method} ${req.url}`);
  next(); // předání řízení
}

// obsluha všech
const handler = function(req, res, next) {
  const { controler, method, params } = req.params;

  // vypsání POST
  console.log(JSON.stringify(req.body));

  res.send(`controler: ${controler}, method: ${method}, method: ${params}`);
  next();
}

app.all('/:controler/:method/:params(*)', [log, handler]);

app.listen(port, () => {
  console.log(`Listening on port ${port}`)
});

Napojení na databázi

Příklad napojení na MySQL databázi následuje.

var mysql = require('mysql2');

var connection = mysql.createConnection({
  host: 'localhost', 
  port: '8889',
  user:'root', 
  password:'root', 
  database:'test'});
  
connection.connect();
  
var queryString = 'SELECT * FROM users';
  
connection.query(queryString, (err, rows, fields) => {
  if (err) throw err;
  for (var i in rows) {
    console.log(`ID: ${rows[i].id}, ${rows[i].name} ${rows[i].surname}`);
  } 
});
  
connection.end();
// poznámka: modul mysql nepodporuje nový typ autentifikace MySQL databáze

Kam dál?

Zejména v poslední době nabírá na popularitě projekt deno (přeházený název node). Autorem je Ryan Dahl, který vytvořil node.js. Hlavní myšlenkou deno je odstranit komplikace v architektuře node.

Zadání

  1. úkol 1

    Implementujte v node.js REST API ze semináře AJAX.