I. Gestion des données▲
À partir de la structure de base que nous avons définie dans l'article précédent, créer des modèles pour l'accès aux données n'est pas très compliqué. Nous allons voir ici comment faire avec MongoDB, mais l'approche serait similaire avec n'importe quel autre SGBDSystème de Gestion de Base de Données ou APIApplication Programming Interface. Voyons cela.
Pour commencer, nous allons sur le site de MongoDB pour télécharger la base que nous décompresserons dans system/mongodb, nous ajouterons également un dossier datas à ce répertoire. Puis, nous allons créer les deux fichiers batch suivant, le premier à la racine et le second dans system.
"
system/mongodb/bin/mongod.exe
"
--dbpath system/mongodb/datas
pause
"
mongodb/bin/mongo.exe
"
Nous lançons le serveur et nous pouvons vérifier avec le client que tout fonctionne.
Nous nous rendons ensuite dans system, pour faire un npm install mongodb afin d'installer le driver MongoDB de Node.js.
Nous allons maintenant essayer d'insérer une donnée à partir du contrôleur index.js pour vérifier que tout fonctionne en reprenant le code d'exemple de la documentation du module :
var
MongoClient =
require
('
mongodb
'
).
MongoClient
,
format =
require
('
util
'
).
format;
MongoClient.
connect
('
mongodb://127.0.0.1:27017/test
'
,
function
(err,
db) {
if
(err) throw
err;
var
collection =
db.
collection
('
test_insert
'
);
collection.
insert
({
a:
2
}
,
function
(err,
docs) {
collection.
count
(function
(err,
count) {
console.
log
(format
("
count
=
%s
"
,
count));
}
);
//
Locate
all
the
entries
using
find
collection.
find
().
toArray
(function
(err,
results) {
console.
dir
(results);
//
Let's
close
the
db
db.
close
();
}
);
}
);
}
)
Pour factoriser cela, nous pourrions ici créer un module qui nous reverrait la connexion demandée, dont les informations seraient stockées dans un fichier de configuration. Nous créerions ensuite un modèle par collection dans lesquels nous placerions nos méthodes d'accès aux données (ces fichiers seraient bien sûr dans le dossier models). Nous finirions par ajouter une méthode model dans app.js que nous pourrions appeler depuis nos contrôleurs pour charger le modèle demandé. Ici, nous allons faire un petit peu plus compliqué, en utilisant l'ODM Mongoose. Pour autant, vous pouvez choisir de vous en passer. Comme le souligne MongoDB dans la documentation :
Because MongoDB is so easy to use, the basic Node.js driver can be the best solution for many applications. However, if you need validations, associations, and other high-level data modeling functions, then an Object Document Mapper may be helpful.
Traduction :
Comme MongoDB est facile à utiliser, le driver de base est la meilleure solution pour beaucoup d'applications. Toutefois, si vous avez besoin de validations, d'associations et d'autres fonctions haut niveau de modélisation de données, un ODM pourrait vous être utile.
II. Intégrer Mongoose▲
Pour installer Mongoose, rien de très compliqué. Il suffit là aussi d'utiliser NPMNode Packaged Modules en faisant un npm install mongoose.
Ce qui va changer un petit peu, c'est que nous allons modifier notre arborescence pour rajouter un niveau pour chaque projet. Comme vous pouvez le voir sur l'image.
Par exemple, demo contient ici deux sous projets api et site, ainsi qu'un dossier datas, qui contiendra notamment la définition de nos schémas de données. Les définir au-dessus poserait problème. En théorie tous nos projets ne manipuleront pas les mêmes données. De la même manière, définir les données à l'intérieur de site, d'api, …, c'est devoir les définir plusieurs fois pour des projets qui utiliseront le même pool de données, c'est donc introduire une redondance de code qui va à l'encontre du DRYDon't Repeat Yourself.
Le dossier datas/config contiendra les dossiers dev, test et prod (un dossier par environnement) et le fichier de configuration entities.json. Dans dev, test et prod nous placerons un fichier db.js. Voici le contenu de ces fichiers :
{
"
main
"
:
{
"
dbms
"
:
"
mongo
"
,
"
host
"
:
"
127.0.0.1
"
,
"
port
"
:
"
27017
"
,
"
base
"
:
"
test
"
,
"
options
"
:
null
}
,
"
example
"
:
{
"
dbms
"
:
"
mysql
"
,
"
host
"
:
"
127.0.0.1
"
,
"
port
"
:
"
3306
"
,
"
base
"
:
"
test
"
,
"
user
"
:
"
demo
"
,
"
pass
"
:
"
hU8@mJ32%1
"
,
"
char
"
:
"
utf8
"
}
}
Ce fichier contiendra la configuration de nos différentes connexions, ici une connexion vers notre base mongo et une base MySQL.
{
"
user
"
:
{
"
connection
"
:
"
main
"
,
"
base
"
:
"
test
"
}
,
"
product
"
:
{
"
connection
"
:
"
main
"
}
}
Dans ce fichier, nous associons une connexion à chaque entité, avec optionnellement la base de données que nous allons interroger.
Nous créons finalement le module db.js dans datas/modules, en voici le contenu :
var
env =
process.
env.
NODE_ENV |
|
'
prod
'
;
var
openDb =
{
}
;
var
enConf =
require
('
../config/entities.json
'
);
var
dbConf =
require
('
../config/
'
+
env+
'
/db.json
'
);
exports.
dbCon =
function
(schema) {
if
(typeof
enConf[
schema]
!
=
=
'
undefined
'
) {
var
dbInfos =
dbConf[
enConf[
schema]
[
'
connection
'
]
]
;
var
base =
enConf[
schema]
[
'
base
'
]
|
|
dbInfos[
'
base
'
]
;
if
($.
isset
(openDb[
base]
)) {
return
openDb[
base]
;
}
else
{
switch
(dbInfos[
'
dbms
'
]
) {
case
'
mongo
'
:
var
mongoose =
$.
require
('
mongoose
'
);
var
dbCon =
mongoose.
createConnection
('
mongodb://
'
+
dbInfos[
'
host
'
]
+
'
:
'
+
dbInfos[
'
port
'
]
+
'
/
'
+
base,
dbInfos[
'
options
'
]
);
openDb[
base]
=
dbCon;
return
dbCon;
break
;
}
}
}
}
III. Création d'un schéma▲
Nous allons maintenant nous rendre dans datas/schemas et réaliser un schéma, ici par exemple un schéma de données représentant un utilisateur :
var
mongoose =
$.
require
('
mongoose
'
)
,
Schema =
mongoose.
Schema;
var
db =
require
('
../modules/db.js
'
);
var
dbCon =
db.
dbCon
('
user
'
);
var
userSchema =
new
Schema
({
_id
: String
,
vanity
: String
,
slug
: {
type:
String
,
unique:
true
}
,
password
: String
,
username
: {
type:
String
,
default
:
'
Anonymous
'
}
,
locale
: String
,
session
: String
,
ip
: String
,
profile
: {
firstname
: String
,
lastname
: String
,
about
: String
,
avatar
: {
type:
String
,
default
:
'
avatar.jpg
'
}
,
gender
: String
,
birthdate
: String
,
country
: String
,
city
: String
,
occupation
: String
,
website
: String
,
biography
: String
}
,
creation
: {
type:
Date
,
default
:
Date
.
now}
}
);
userSchema.
methods.
findUserFromSlug =
function
(cb) {
return
this
.
model
('
User
'
).
find
({
slug:
this
.
slug }
,
cb);
}
userSchema.
virtual
('
profile.fullname
'
).
get
(function
() {
return
this
.
profile.
firstname +
'
'
+
this
.
profile.
lastname;
}
);
module.
exports =
dbCon.
model
('
User
'
,
userSchema);
Pour plus d'information sur la définition des schémas, je vous renvoie vers la documentation officielle de Mongoose.
IV. Création d'un modèle▲
Nous allons ensuite dans models créer un modèle user.js :
var
app =
require
(module.
parent.
id);
exports.
create =
function
(datas) {
var
User =
app.
entity
('
user
'
);
new
User
(datas).
save
();
}
exports.
read =
function
(conditions,
callback) {
var
User =
app.
entity
('
user
'
);
User.
findOne
(conditions,
function
(err,
user) {
if
(err) throw
err;
callback
(user);
}
);
}
exports.
update =
function
(conditions,
datas,
callback) {
var
User =
app.
entity
('
user
'
);
User.
findOneAndUpdate
(conditions,
datas,
{
'
new
'
:
true
}
,
function
(err,
user) {
if
(err) throw
err;
callback
(user);
}
);
}
exports.
delete
=
function
(conditions,
callback) {
var
User =
app.
entity
('
user
'
);
User.
remove
(conditions,
function
(err) {
if
(err) throw
err;
callback
();
}
);
}
V. Accès aux modèles depuis les contrôleurs▲
Enfin, dans un de nos contrôleurs, nous pourrions trouver les codes suivant :
var
user =
ctx.
params.
post.
user;
var
userModel =
app.
model
('
user
'
);
user =
$.
merge
(user,
{
_id
: slug+
'
@
'
+
app.
site.
domain,
slug
: S
(user.
vanity).
slugify
().
s,
locale
: ctx.
client.
locale,
session
: ctx.
client.
id,
ip
: ctx.
client.
ip}
);
userModel.
create
(user);
var
userModel =
app.
model
('
user
'
);
userModel.
update
({
session:
ctx.
client.
id}
,
ctx.
params.
post.
user,
function
(user) {
console.
log
(user);
res.
end
();
}
);
Vous avez peut-être remarqué ici que nous avons placé la session au niveau de Mongo. C'est un choix de conception relativement discutable, l'idéal étant plutôt ici de gérer la session indépendamment avec Memcache ou Redis.
VI. Ajout de librairies externes au niveau global▲
Vous vous rappelez peut-être que dans la première partie, nous avions créé une variable globale $ contenant par la suite plusieurs fonctions que nous avons ajoutées dans framework.js. Dans le paragraphe précédant, vous avez vu apparaître une variable globale S utilisée pour manipuler les chaînes de caractères. En faisant cela, notre but est de proposer tout un ensemble de méthodes puissantes et utiles accessibles partout depuis toutes nos applications sans avoir besoin de charger spécifiquement un module. Inconvénient, notre contexte global peut être écrasé. On évitera cela en utilisant Object.defineProperties.
À ce stade, j'ai intégré trois librairies :
- $, le framework ;
- _, Lo-Dash, une réécriture d'underscore, plus rapide et plus complète ;
- S, string.js, une librairie de manipulation de chaines de caractères.
L'ajout de ces trois librairies se fait tout simplement ainsi en haut de notre fichier server.js :
Object
.
defineProperties
(global,
{
"
_
"
:
{
value
: require
('
lodash
'
)
}
,
"
$
"
:
{
value
: require
('
core
'
)
}
,
"
S
"
:
{
value
: require
('
string
'
)
}
}
);
Et nous plaçons notre fonction define dans framework.js
exports.
define =
function
(property,
value,
scope) {
Object
.
defineProperty
(scope,
property,
{
value
: value,
enumerable
: true
}
);
}
$.merge que vous avez rencontré au dessus est un adapteur de _.merge de la librairie Lo-Dash (pattern adaptor). À ce stade, tout votre code a ainsi accès à ces librairies.
VII. Conclusion▲
Ayant perdu une version précédente, cette partie est un peu plus courte et moins ambitieuse que prévu. Je ne sais pas encore de quoi traitera la prochaine. Temps réel avec socket.io, packaging du framework et partage sur GitHub, ajout de fonctionnalité HTTP avancées (compression, gestion des eTags…), tests et debogage, ou autre chose. Nous verrons bien, suite dans la prochaine partie.
Remerciements▲
Cet article a été publié avec l'aimable autorisation de Julien Alric. L'article original (Créer un framework avec Node.js – 2nd partie) peut être vu sur le blog de Julien Alric.
Nous tenons à remercier XXX pour sa relecture attentive de cet article.
N'hésitez pas à commenter cet article sur le forum. Commentez