JavaScript na serveri rýchlo a jednoducho so slovenským Total.js frameworkom

JavaScript + Total.js + Node.js
JavaScript na serveri s Total.js frameworkom na Node.js platforme.

Nebudem písať dlhé omáčky, ale prejdem rovno k veci. Pokiaľ patríte k lenivým programátorom ako som ja, tak určite oceníte náš domáci Total.js framework, ktorý Vám ponúka množstvo zaujímavých funkcií na tvorbu webových stránok, webových služieb, webových a intranetových appiek.

Začíname

Garantujem Vám, že Vašu prvú webovú aplikáciu v Node.js a Total.js budete mať napísanú do 5 minút. Programujem už veľmi veľa rokov a s určitosťou môžem povedať, že inštalácia celého tohto prostredia je najjednoduchšia a najrýchlejšia zo všetkého čo poznám.

package.json:

{
  "name": "hello-world",
  "version": "1.0.0",
  "main": "index.js",
  "dependencies": {
    "total5": "latest"
  },
  "scripts": {
    "start": "node index.js 8000"
  }
}
/hello-world/package.json

index.js:

// Toto je spúšťací súbor Total.js frameworku.

require('total5');
Total.run({});
/hello-world/index.js

Keď to máte, tak si vytvorte adresár /hello-world/controllers/ a do neho vytvorte súbor api.js:

ROUTE('GET /', function($) {

    // console.log($.ip); // IP adresa klienta
    // console.log($.ua); // Vyparsovaný user-agent
    
    $.json({ hello: 'world' });
    
    // Ďalšie možnosti:
    // $.text('Hello world');
    // $.html('Hello <b>world</b>!');
    // $.redirect('https://www.totaljs.com');
    // $.file('/path/to/file.json');
    // $.empty();
    // $.invalid('Popis chyby');
    
    // Ako spracovať query argumenty?
    // $.query {Object}
    // $.query.search // ?search=Total
    
    // Ako sa dostať ku cookies?
    // $.cookie('nazov')
    
    // Ako zapísať cookie?
    // $.cookie('nazov', 'hodnota', '2 days');

    // Ako sa dostať k request hlavičkám?
    // $.headers['x-token']
    
});
/hello-world/controllers/api.js

A keď už toto máte, tak si otvorte terminál/príkazový riadok v adresáry /hello-world/ a napíšte:

# "npm" utilita sa nainštalovala s Node.js platformou.

# Spúšťa sa iba prvý krát na nainštalovanie závislostí.
# Závislosti sa nainštalujú do adresára "node_modules"
npm install

# Spustenie aplikácie.
npm start

# Poznámka: aplikáciu je možné spustiť aj cez príkaz:
# node index.js 8000
# "8000" znamená v tomto prípade číslo portu
Príkaz do terminálu / príkazového riadku

Ak všetko zbehlo, tak si otvorte webový prehliadač a zadajte URL adresu: http://127.0.0.1:8000. Na tejto adrese by sa Vám mal zobraziť výstup z Total.js aplikácie.


Total.js má v sebe veľmi veľkú funkčnosť a dokonca aj automaticky reaguje na zmeny v zdrojom kóde. Pokiaľ urobíte zmenu v controllers, tak sa framework automaticky reštartne. Takže kľudne si môžete otvoriť projekt a priamo vykonávať zmeny bez dodatočných príkazov v terminály alebo v príkazovom riadku.

Dôležité: Total.js má v sebe zabudovaný webový server, takže v prípade, že chcete prevádzkovať viacej Total.js aplikácií súbežne, tak každá aplikácia musí bežať na inom porte. Toto sa na serveri rieši tzv. reverznou proxy (o tom inokedy).

Pokročilejšie funkcie

Ak ste sa dočítali až sem, tak sa ešte trochu pohráme so základnou funkčnosťou frameworku.

Rozšírime súbor /controllers/api.js o novú funkčnosť, takže nižšie uvedené časti zdrojového kódu skopírujte do spomínaného súboru.

Dynamické parametre a SPA routing:

// Parametrická routa 1
ROUTE('GET /products/{category}/', function($) {
    $.text('Category: ' + $.params.category);
});

// Parametrická routa 2
ROUTE('GET /products/{category}/{id}/', function($) {
    $.text('Category: {0}, id: {1}'.format($.params.category, $.params.id));
});

// Wildcard (SPA) routing
// Táto akcia spracuje všetky requesty od endpointu /admin/
ROUTE('GET /admin/*', function($) {
    $.text($.url);
});

In-memory proxy na iný server:

ROUTE('GET /rss/', function($) {
    $.proxy('https://blog.totaljs.com/rss/');
});

Po uložení navštívte stránku http://127.0.0.1:8000/rss/ a mali by ste vidieť obsah RSS feedu z Total.js blogu.

Spracovanie dát:

// Total.js spracuje JSON alebo urlencoded payload automaticky
// Maximálny limit (by default) je 5 kB na celý payload
ROUTE('POST /form/', function($) {
   
    // Payload je uložený v:
    // $.body {Object}

    // Klientovi vrátime v JSONe to, čo nám poslal
    $.json($.body);
    
});

// Ako viem rozšíriť maximálny limit napríklad na 1 MB?
// ROUTE('POST /form/ <1MB ', function($) { ... });

Parsovanie HTML obsahu cez Total.js HTML parser (SHMU.sk):

// Vyparsujeme aktuálne počasie zo SHMU stránky
ROUTE('GET /pocasie/', async function($) {

    let html = await RESTBuilder.GET('https://www.shmu.sk').raw().promise($);
    let htmlparser = html.parseHTML();

    // Vyhľadáme všetky elementy s classou ".map-point"
    let points = htmlparser.find('.map-point');
    let response = [];

    // V nájdených bodoch vyhľadáme špecifické hodnoty
    for (let point of points) {
        let city = {};
        city.name = point.find('.map-city')[0].text();
        city.temperature = point.find('.map-temp-w')[0].text();
        response.push(city);
    }

    $.json(response);
});

Spracovanie používateľských súborov (file upload):

ROUTE('POST /upload/ @upload <5MB', function($) {

    for (let file of $.files)
        console.log(file);

    $.success();

});

Pozdržanie odpovede na 2 sekundy:

// Pozor: Total.js má v sebe mechanizmus na auto-odpoveď v prípade, že akcia neodpovie klientovi (by default: 5 sekúnd)

ROUTE('GET /delay/', function($) {

    setTimeout(() => $.success(), 2000);

});

// Predvolený timeout sa dá rozšíriť napr. na 20 sekúnd nižšie uvedeným zápisom
// ROUTE('GET /delay/ <20s', function($) { ... });

Spustenie ďalšej treťostranovej applikácie a vrátenie odpovede na request:

// v MacOS musíte mať spustenú appku pod "sudo"
ROUTE('GET /ping/', async function($) {
    try {
	    let response = await SHELL('ping -c 3 www.totaljs.com');
        $.text(response);
    } catch (e) {
        $.invalid(e);
    }
});

Načítanie a upravenie odpovede z externej služby:

ROUTE('GET /json/', async function($) {

    // Načítanie externého JSONu
    let response = await RESTBuilder.GET('https://www.totaljs.com/dashboard.json').promise($);

    // Upravíme odpoveď:
    response.extended = true;

    // Vrátime klientovi
    $.json(response);
});

Real-time streamovanie CSV/XML dát:

ROUTE('GET /csv/ <20s', function($) {
    // Urobíme request na CSV súbor, ktorý budeme streamovať po častiach.
    // Nižšie uvedený kód bude fungovať aj s gigovými datami.
    RESTBuilder.GET('https://raw.githubusercontent.com/datasets/world-cities/refs/heads/main/data/world-cities.csv').stream(function(err, response) {

        // Error handling v prípade, že služba z nejakého dôvodu padne.
        if (err) {
            $.invalid(err);
            // $.invalid('Služba je dočasne nedostupná');
            return;
        }

        let countries = {};

        // Pomocou metódy Utils.streamer() sa dá efektívne streamovať CSV, XML alebo podobné typy obsahu.
        // Streamer potrebuje vedieť, čo je oddeľovač dát (funkcia hľadá v bufferi (bytes) oddeľovač).
        response.stream.on('data', Utils.streamer('\n', function(line, index) {
            
            // Vyparsujeme CSV riadok
            var csv = line.parseCSV()[0];
            if (countries[csv.b])
                countries[csv.b]++;
            else
                countries[csv.b] = 1;

        }, 1)); // 1 znamená, že preskoč "1" riadok

        // Keď sa skončí stream, vrátime odpoveď
        response.stream.on('end', () => $.json(countries));
    });
    
    // Ako by sa parsovalo XML?
    // response.stream.on('data', Utils.streamer('<CD>', '</CD>', function(line, index) { ... }));
});

Spracúvame statické súbory

// Táto routa spracuje všetky .jpg obrázky na ceste /photos/*.jpg.
// Na každý jeden obrázok odpovie rovnakým obrázkom zo servera pixabay.com.
ROUTE('FILE /photos/*.jpg', function($) {

    // http://127.0.0.1:8000/photos/peter.jpg
    // filename = peter.jpg
    let filename = $.split[1];

    RESTBuilder.GET('https://cdn.pixabay.com/photo/2024/09/27/22/44/owl-9079962_640.jpg').stream(function(err, response) {
        if (err) {
            $.invalid(err);
        } else {
            // Prestreamujeme data z ďalšieho servera
            $.stream(response.headers['content-type'], response.stream);
        }
    });

});

Záver

Týmto blogom som chcel iba ukázať základnú funkčnosť Total.js frameworku. V ďalších blogoch sa posunieme ďalej a ukážem Vám ďalšie zaujímavé funkcie v Total.js Platforme.