Node.js i MySQL oraz MongoDB

node-js

Poziom zaawansowany

Po wprowadzeniu do node.js pora na bardziej zaawansowane i praktyczne informacje. Dziś zajmiemy się komunikacją node.js z bazami danych MySQL, a także wspomnimy o MongoDB.

Node.js i MySQL

Jeżeli programujemy np. w PHP, mamy zapewne w systemie środowisko LAMP, mamy więc też MySQL. Potrzebujemy jeszcze tylko node.js oraz modułu MySQL:

$ npm install mysql

Konfiguracja

Dla wygody, stwórzmy sobie od razu plik konfiguracyjny config.js:

var config = {};
config.db = {};

config.db.type = 'mysql';
config.db.charset = 'utf8';

config.db.username = 'user';
config.db.password = 'pass';
config.db.host = 'localhost';
config.db.dbname = 'node_tests'; // DB name

config.db.users_tbl = 'users'; // table name
// config.db.another_tbl = 'next_table'; // ...

// export
module.exports = config;

Stworzyliśmy prosty obiekt przechowujący konfigurację. Ważne jest, aby go „wyeksportować”:

module.exports = config;

Napiszmy zatem testowy skrypt, który połączy się z bazą danych.

Przykład użycia:

var mysql = require('mysql');
var config = require('./config.js');

var db_access = {
    host     : config.db.host,
    user     : config.db.username,
    password : config.db.password,
    database : config.db.dbname
};

var tbl = config.db.users_tbl;

var conn = mysql.createConnection(db_access);
conn.connect();

var queryString = 'SELECT * FROM ' + tbl;

conn.query(queryString, function (err, rows, fields) {
    if (err) { throw err; }

    for (var i in rows) {
        console.log('User names: ', rows[i].name);
    }
});

conn.end();

Kod jest schludny, a jego zadaniem jest pobranie i wyświetlenie użytkowników z naszej testowej bazy danych (dla celów testowych wystarczy tabela z polami ID użytkownika oraz firstname).

Zaczynamy od dołączenia wymaganych modułów – mysql oraz naszej konfiguracji. Następnie tworzymy obiekt dostępu do bazy, którego wymaga funkcja createConnection(). Obiekt ten musi zawierać pola host, user, password, database. Dane te pobraliśmy z naszego pliku konfiguracyjnego.

Nawiązujemy połączenie i wykonujemy zapytanie – conn.query(). Na końcu zamykamy połączenie.

Obsługa zdarzeń

To sposób na obsługę poszczególnych przypadków np. otrzymanie danych gotowych do przetworzenia, wystąpienie błędu, etc.


var query = connection.query(queryString);

query.on('result', function(row) {
    console.log(row.firstname);
    
    /*
    connection.pause();
    // do some more processing on the row ...
    console.log(row);
    connection.resume();
    */
});

query.on('error', function(err) {
    throw err;
});

connection.on('close', function(err) {
  if (err) {
    // unexpected closing of connection - reconnect back
    connection = mysql.createConnection(connection.config);
  } else {
    console.log('Connection closed');
  }
});

connection.end();

Przykład pokazuje obsługę poszczególnych zdarzeń dla naszego zapytania.

Node.js i tworzenie bazy danych oraz tabel

Nie ma problemu z wykonywaniem innych zapytań, takich jak tworzenie baz i tabel, co ilustrują poniższe przykłady:


connection.query('CREATE DATABASE foobar');

connection.changeUser({database: 'foobar'});

// lub
connection.query('USE foobar')

// struktura tabeli
connection.query('CREATE TABLE foobar.mytable(' +
  'id INT NOT NULL AUTO_INCREMENT, ' +
  'author VARCHAR( 128 ) NOT NULL, ' +
  'quote TEXT NOT NULL, PRIMARY KEY ( id )' +
')');


SQL injection, bezpieczeństwo, stabilność

To kwestie z pewnością dobrze znane back-end developerom.

Możemy co prawda posłużyć się funkcją connection.escape:


connection.connect();
 
var key = 'something...';
var queryString = 'SELECT * FROM posts WHERE key = ' 
  + connection.escape(key);

connection.query(queryString, function(err, rows, fields) {
  ...
});

Jednak załóżmy że implementujemy funkcję dodawania komentarzy, lub inną, gdzie używamy tekstu pobranego od użytkownika w zapytaniu SQL Insert.

Musimy się odpowiednio zająć tymi danymi, ponieważ nie chodzi tylko o security, ale również o stabilność naszej aplikacji. Np. apostrofy w tekście mogą spowodować błąd syntaktyczny zapytania, co może przerwać działanie całego skryptu node.js.

Możemy więc bez większych problemów napisać własne rozwiązania, np. funkcja sanitize(), lub też dobrze znana z PHP, funkcja addslashes (biblioteka php.js).

Przykład implementacji:

// mytools.js
var mytools = {

  sanitize: function (html) {
    return String(html)
      .replace(/&(?!\w+;)/g, '&')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;');
  },

  addslashes: function (str) {
    return (str + '')
      .replace(/[\\"']/g, '\\$&')
      .replace(/\u0000/g, '\\0');
  }
};

module.exports = mytools;

Przykład użycia

var tools = require('./mytools.js');

var comment = tools.sanitize(res_data.comment);
// comment = tools.addslashes(comment);

W ten sposób możemy bezpiecznie użyć zmiennej w zapytaniu. Ominiemy zarówno dodawania niebezpiecznych elementów (np. skryty JS w treści), jak i problemów z wykonaniem zapytania (apostrofy w tekście).

Przykład zapytania node.js i MySQL – Insert:


function insertCommentData(socket, our_data) {

  var connection = mysql.createConnection(db_access);
  connection.connect();

  var clean = tools.addslashes(socket.content);

  var q = "INSERT INTO " + comments_tbl;
  q += " (user_id, content, created_at)";
  q += " VALUES (" + socket.user_id + ", " + clean + ", NOW() )";
  // console.log(q);

  connection.query(q, function (qe, qr) {
    if (qe) { console.log(qe); }
    
    // do something with result ...

    connection.end();
  });
}

Wykonując zapytanie możemy wykonać jakieś operacje na wyniku i np. zamknąć połączenie, jeśli nie będzie na dłużej potrzebne. Zmienne qe oraz qr to odpowiednio błędy zapytania (jeśli występują) oraz wyniki.

Bardzo przydatną informacją z wyniku zapytania może być ID ostatnio dodanego rekordu (last insert ID), które chcemy dalej przetworzyć.

Przykład – last insert ID:


...
connection.query(q, function (qe, qr) {
  if (qe) { console.log(qe); }

  // add new information to the object
  our_data.comment_id = qr.insertId;

  // send this object to all sockets
  io.sockets.emit("e_new_comment", {
    message: our_data
  });
  
  connection.end();
});

Przykłady zakładają przesyłanie danych poprzez gniazdka. O socket.io napiszemy więcej w kolejnym artykule.

Zapytanie SELECT COUNT

Pokażemy teraz jak łatwo wykonać i pobrać wynik zapytania SELECT COUNT, które pozwala policzyć rekordy spełniające kryteria zapytania. Zależnie od tego czy takie rekordy istnieją w bazie, czy nie, możemy chcieć wykonać zapytanie INSERT (dodaj) lub UPDATE (aktualizuj rekord).

Przykład:


function setStatus(user_id, status) {

  var connection = mysql.createConnection(db_access);
  connection.connect();

  var q1 = "SELECT COUNT(1) AS cnt FROM " + user_status_tbl + " WHERE user_id = '" + user_id + "'";

  connection.query(q1, function (err, rows, fields) {
    if (err) { console.log(err); }

    if ((rows[0].cnt) > 0) {
      // update
      var q2 = "UPDATE " + user_status_tbl + " SET status = " + status + ", updated_at = NOW() ";
      q2 += "WHERE user_id = '" + user_id + "' ";

      connection.query(q2, function (qe, qr) {
        if (qe) { console.log(qe); }

        console.log('Updated #' + user_id);

        connection.end();
      });
    } else {
      // add
      var q2 = "INSERT INTO " + user_status_tbl + " (user_id, updated_at, status) ";
      q2 += "VALUES (" + user_id + ", NOW(), " + status + ")";

      connection.query(q2, function (qe, qr) {
        if (qe) { console.log(qe); }

        console.log('Inserted #' + user_id);

        connection.end();
      });
    }
  });
}

Nadaliśmy wynikowi alias „cnt”, wiec odwołujemy się do niego: rows[0].cnt

Z tymi podstawami jesteśmy właściwie gotowi, aby efektywnie pracować z node.js i MySQL.

Będąc przy temacie baz danych, spójrzmy na inną, bardzo popularną w przypadku node.js bazę MongoDB.

Node.js i MongoDB

Mongo to otwarty, nierelacyjny system zarządzania bazą danych. Charakteryzuje się dużą skalowalnością oraz brakiem ściśle zdefiniowanej struktury obsługiwanych baz danych.

Dane składowane są jako dokumenty w formacie JSON, co umożliwia aplikacjom bardziej naturalne przetwarzanie, przy zachowaniu możliwości tworzenia hierarchii oraz indeksowania. Jest to bardzo wydajny system.

Przygotowanie

Instalujemy MongoDB dla naszego systemu.

Np. w Windows:

$ cd C:\Program Files\MongoDB 2.6 Standard\bin

$ md \data\md

$ mongod

Nowa baza:

$ mongod –dbpath C:\Projects\node\catalog1\data

Uruchamiamy kolejną konsole i łączymy się z bazą:

$ cd C:\Program Files\MongoDB 2.6 Standard\bin

$ mongo

W konsoli mongo piszemy:

$ use catalog1

Wybraliśmy bazę danych. Możemy teraz w konsoli wprowadzić testowe dane:

db.usercollection.insert({ "username" : "testuser1", "email" : "testuser1@testdomain.com" })
// ...

Pobieranie danych (funkcja pretty() dodaje linebreaks):

$ db.usercollection.find().pretty()

Aby zapisać dane trwale używamy save():

db.usercollection.save({ "username" : "testuser1", "email" : "testuser1@testdomain.com" })
// ...

Jak możemy zobaczyć w wynikach wyświetlanych w konsoli, MongoDB potrafi samodzielnie zarządzać kluczami ID obiektów w bazie.

Wydawnictwo Strefa Kursów

Usuwanie wszystkich danych:

db.dropDatabase();

Do pracy z MongoDB z poziomu node.js będziemy potrzebować modułów mongodb oraz na przykład monk:

var mongo = require('mongodb');
var monk = require('monk');
var db = monk('localhost:27017/catalog1');
...

W jednym z kolejnych tutoriali napiszemy o express.js oraz popracujemy więcej z MongoDB.

Node.js i MySQL – podsumowanie

Staraliśmy się przedstawić kluczowe kwestie potrzebne do pracy z MySQL w node.js, całość zakończona wprowadzeniem do MongoDB. Zapraszamy do kolejnych artykułów na temat wspaniałej technologii node.js, które są już w przygotowaniu.

Programista WWW i aplikacji mobilnych z wieloletnim doświadczeniem. Bloger 🙂 Anty-lewak 🙂 Pasjonat programowania, nowych technologii, a także sportu i motoryzacji.

Twitter LinkedIn Google+ Skype Xing 

Podaj dalej: Share on Facebook1Tweet about this on TwitterShare on Google+0Share on LinkedIn0Share on Tumblr0Digg thisEmail this to someonePin on Pinterest1
Możesz skomentować leave a response, lub podać trackback z własnej strony.